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    }