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 }