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