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("org.objectweb.asm."); 053 addTransformerExclusion("com.google.common."); 054 addTransformerExclusion("cpw.mods.fml.common.asm.SideOnly"); 055 addTransformerExclusion("cpw.mods.fml.common.Side"); 056 } 057 058 public void registerTransformer(String transformerClassName) 059 { 060 try 061 { 062 transformers.add((IClassTransformer) loadClass(transformerClassName).newInstance()); 063 } 064 catch (Exception e) 065 { 066 FMLRelaunchLog.log(Level.SEVERE, e, "A critical problem occured registering the ASM transformer class %s", transformerClassName); 067 } 068 } 069 @Override 070 public Class<?> findClass(String name) throws ClassNotFoundException 071 { 072 // NEI/CCC compatibility code 073 if (excludedPackages.length != 0) 074 { 075 classLoaderExceptions.addAll(Arrays.asList(excludedPackages)); 076 excludedPackages = new String[0]; 077 } 078 if (transformerExclusions.length != 0) 079 { 080 transformerExceptions.addAll(Arrays.asList(transformerExclusions)); 081 transformerExclusions = new String[0]; 082 } 083 084 for (String st : classLoaderExceptions) 085 { 086 if (name.startsWith(st)) 087 { 088 return parent.loadClass(name); 089 } 090 } 091 092 if (cachedClasses.containsKey(name)) 093 { 094 return cachedClasses.get(name); 095 } 096 097 for (String st : transformerExceptions) 098 { 099 if (name.startsWith(st)) 100 { 101 Class<?> cl = super.findClass(name); 102 cachedClasses.put(name, cl); 103 return cl; 104 } 105 } 106 107 try 108 { 109 int lastDot = name.lastIndexOf('.'); 110 if (lastDot > -1) 111 { 112 String pkgname = name.substring(0, lastDot); 113 if (getPackage(pkgname)==null) 114 { 115 definePackage(pkgname, null, null, null, null, null, null, null); 116 } 117 } 118 byte[] basicClass = getClassBytes(name); 119 byte[] transformedClass = runTransformers(name, basicClass); 120 Class<?> cl = defineClass(name, transformedClass, 0, transformedClass.length); 121 cachedClasses.put(name, cl); 122 return cl; 123 } 124 catch (Throwable e) 125 { 126 throw new ClassNotFoundException(name, e); 127 } 128 } 129 130 public byte[] getClassBytes(String name) throws IOException 131 { 132 InputStream classStream = null; 133 try 134 { 135 URL classResource = findResource(name.replace('.', '/').concat(".class")); 136 if (classResource == null) 137 { 138 return null; 139 } 140 classStream = classResource.openStream(); 141 return readFully(classStream); 142 } 143 finally 144 { 145 if (classStream != null) 146 { 147 try 148 { 149 classStream.close(); 150 } 151 catch (IOException e) 152 { 153 // Swallow the close exception 154 } 155 } 156 } 157 } 158 159 private byte[] runTransformers(String name, byte[] basicClass) 160 { 161 for (IClassTransformer transformer : transformers) 162 { 163 basicClass = transformer.transform(name, basicClass); 164 } 165 return basicClass; 166 } 167 168 @Override 169 public void addURL(URL url) 170 { 171 super.addURL(url); 172 sources.add(url); 173 } 174 175 public List<URL> getSources() 176 { 177 return sources; 178 } 179 180 181 private byte[] readFully(InputStream stream) 182 { 183 try 184 { 185 ByteArrayOutputStream bos = new ByteArrayOutputStream(stream.available()); 186 int r; 187 while ((r = stream.read()) != -1) 188 { 189 bos.write(r); 190 } 191 192 return bos.toByteArray(); 193 } 194 catch (Throwable t) 195 { 196 /// HMMM 197 return new byte[0]; 198 } 199 } 200 201 public List<IClassTransformer> getTransformers() 202 { 203 return Collections.unmodifiableList(transformers); 204 } 205 206 private void addClassLoaderExclusion(String toExclude) 207 { 208 classLoaderExceptions.add(toExclude); 209 } 210 211 void addTransformerExclusion(String toExclude) 212 { 213 transformerExceptions.add(toExclude); 214 } 215 }