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