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 }