001 package cpw.mods.fml.relauncher; 002 003 import java.io.ByteArrayOutputStream; 004 import java.io.IOException; 005 import java.io.InputStream; 006 import java.net.URL; 007 import java.net.URLClassLoader; 008 import java.util.ArrayList; 009 import java.util.Arrays; 010 import java.util.Collections; 011 import java.util.HashMap; 012 import java.util.HashSet; 013 import java.util.List; 014 import java.util.Map; 015 import java.util.Set; 016 import java.util.logging.Level; 017 018 public class RelaunchClassLoader extends URLClassLoader 019 { 020 // Left behind for CCC/NEI compatibility 021 private static String[] excludedPackages = new String[0]; 022 // Left behind for CCC/NEI compatibility 023 private static String[] transformerExclusions = new String[0]; 024 025 private List<URL> sources; 026 private ClassLoader parent; 027 028 private List<IClassTransformer> transformers; 029 private Map<String, Class> cachedClasses; 030 031 private Set<String> classLoaderExceptions = new HashSet<String>(); 032 private Set<String> transformerExceptions = new HashSet<String>(); 033 034 public RelaunchClassLoader(URL[] sources) 035 { 036 super(sources, null); 037 this.sources = new ArrayList<URL>(Arrays.asList(sources)); 038 this.parent = getClass().getClassLoader(); 039 this.cachedClasses = new HashMap<String,Class>(1000); 040 this.transformers = new ArrayList<IClassTransformer>(2); 041 // ReflectionHelper.setPrivateValue(ClassLoader.class, null, this, "scl"); 042 Thread.currentThread().setContextClassLoader(this); 043 044 // standard classloader exclusions 045 addClassLoaderExclusion("java."); 046 addClassLoaderExclusion("sun."); 047 addClassLoaderExclusion("cpw.mods.fml.relauncher."); 048 addClassLoaderExclusion("net.minecraftforge.classloading."); 049 050 // standard transformer exclusions 051 addTransformerExclusion("javax."); 052 addTransformerExclusion("cpw.mods.fml."); 053 addTransformerExclusion("org.objectweb.asm."); 054 addTransformerExclusion("com.google.common."); 055 } 056 057 public void registerTransformer(String transformerClassName) 058 { 059 try 060 { 061 transformers.add((IClassTransformer) loadClass(transformerClassName).newInstance()); 062 } 063 catch (Exception e) 064 { 065 FMLRelaunchLog.log(Level.SEVERE, e, "A critical problem occured registering the ASM transformer class %s", transformerClassName); 066 } 067 } 068 @Override 069 public Class<?> findClass(String name) throws ClassNotFoundException 070 { 071 // NEI/CCC compatibility code 072 if (excludedPackages.length != 0) 073 { 074 classLoaderExceptions.addAll(Arrays.asList(excludedPackages)); 075 excludedPackages = new String[0]; 076 } 077 if (transformerExclusions.length != 0) 078 { 079 transformerExceptions.addAll(Arrays.asList(transformerExclusions)); 080 transformerExclusions = new String[0]; 081 } 082 083 for (String st : classLoaderExceptions) 084 { 085 if (name.startsWith(st)) 086 { 087 return parent.loadClass(name); 088 } 089 } 090 091 if (cachedClasses.containsKey(name)) 092 { 093 return cachedClasses.get(name); 094 } 095 096 for (String st : transformerExceptions) 097 { 098 if (name.startsWith(st)) 099 { 100 Class<?> cl = super.findClass(name); 101 cachedClasses.put(name, cl); 102 return cl; 103 } 104 } 105 106 try 107 { 108 int lastDot = name.lastIndexOf('.'); 109 if (lastDot > -1) 110 { 111 String pkgname = name.substring(0, lastDot); 112 if (getPackage(pkgname)==null) 113 { 114 definePackage(pkgname, null, null, null, null, null, null, null); 115 } 116 } 117 byte[] basicClass = getClassBytes(name); 118 byte[] transformedClass = runTransformers(name, basicClass); 119 Class<?> cl = defineClass(name, transformedClass, 0, transformedClass.length); 120 cachedClasses.put(name, cl); 121 return cl; 122 } 123 catch (Throwable e) 124 { 125 throw new ClassNotFoundException(name, e); 126 } 127 } 128 129 public byte[] getClassBytes(String name) throws IOException 130 { 131 InputStream classStream = null; 132 try 133 { 134 URL classResource = findResource(name.replace('.', '/').concat(".class")); 135 if (classResource == null) 136 { 137 return null; 138 } 139 classStream = classResource.openStream(); 140 return readFully(classStream); 141 } 142 finally 143 { 144 if (classStream != null) 145 { 146 try 147 { 148 classStream.close(); 149 } 150 catch (IOException e) 151 { 152 // Swallow the close exception 153 } 154 } 155 } 156 } 157 158 private byte[] runTransformers(String name, byte[] basicClass) 159 { 160 for (IClassTransformer transformer : transformers) 161 { 162 basicClass = transformer.transform(name, basicClass); 163 } 164 return basicClass; 165 } 166 167 @Override 168 public void addURL(URL url) 169 { 170 super.addURL(url); 171 sources.add(url); 172 } 173 174 public List<URL> getSources() 175 { 176 return sources; 177 } 178 179 180 private byte[] readFully(InputStream stream) 181 { 182 try 183 { 184 ByteArrayOutputStream bos = new ByteArrayOutputStream(stream.available()); 185 int r; 186 while ((r = stream.read()) != -1) 187 { 188 bos.write(r); 189 } 190 191 return bos.toByteArray(); 192 } 193 catch (Throwable t) 194 { 195 /// HMMM 196 return new byte[0]; 197 } 198 } 199 200 public List<IClassTransformer> getTransformers() 201 { 202 return Collections.unmodifiableList(transformers); 203 } 204 205 private void addClassLoaderExclusion(String toExclude) 206 { 207 classLoaderExceptions.add(toExclude); 208 } 209 210 void addTransformerExclusion(String toExclude) 211 { 212 transformerExceptions.add(toExclude); 213 } 214 }