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.List;
013    import java.util.Map;
014    import java.util.logging.Level;
015    
016    public class RelaunchClassLoader extends URLClassLoader
017    {
018        private static String[] excludedPackages = {
019            "java.", "sun.", "javax.",
020            "cpw.mods.fml.relauncher.", "net.minecraftforge.classloading."
021        };
022    
023        private static String[] transformerExclusions =
024        {
025            "org.objectweb.asm.", "com.google.common.", "cpw.mods.fml."
026        };
027        private List<URL> sources;
028        private ClassLoader parent;
029    
030        private List<IClassTransformer> transformers;
031        private Map<String, Class> cachedClasses;
032    
033        public RelaunchClassLoader(URL[] sources)
034        {
035            super(sources, null);
036            this.sources = new ArrayList<URL>(Arrays.asList(sources));
037            this.parent = getClass().getClassLoader();
038            this.cachedClasses = new HashMap<String,Class>(1000);
039            this.transformers = new ArrayList<IClassTransformer>(2);
040    //        ReflectionHelper.setPrivateValue(ClassLoader.class, null, this, "scl");
041            Thread.currentThread().setContextClassLoader(this);
042        }
043    
044        public void registerTransformer(String transformerClassName)
045        {
046            try
047            {
048                transformers.add((IClassTransformer) loadClass(transformerClassName).newInstance());
049            }
050            catch (Exception e)
051            {
052                FMLRelaunchLog.log(Level.SEVERE, e, "A critical problem occured registering the ASM transformer class %s", transformerClassName);
053            }
054        }
055        @Override
056        public Class<?> findClass(String name) throws ClassNotFoundException
057        {
058            for (String st : excludedPackages)
059            {
060                if (name.startsWith(st))
061                {
062                    return parent.loadClass(name);
063                }
064            }
065    
066            if (cachedClasses.containsKey(name))
067            {
068                return cachedClasses.get(name);
069            }
070    
071            for (String st : transformerExclusions)
072            {
073                if (name.startsWith(st))
074                {
075                    Class<?> cl = super.findClass(name);
076                    cachedClasses.put(name, cl);
077                    return cl;
078                }
079            }
080    
081            try
082            {
083                int lastDot = name.lastIndexOf('.');
084                if (lastDot > -1)
085                {
086                    String pkgname = name.substring(0, lastDot);
087                    if (getPackage(pkgname)==null)
088                    {
089                        definePackage(pkgname, null, null, null, null, null, null, null);
090                    }
091                }
092                byte[] basicClass = getClassBytes(name);
093                byte[] transformedClass = runTransformers(name, basicClass);
094                Class<?> cl = defineClass(name, transformedClass, 0, transformedClass.length);
095                cachedClasses.put(name, cl);
096                return cl;
097            }
098            catch (Throwable e)
099            {
100                throw new ClassNotFoundException(name, e);
101            }
102        }
103    
104        /**
105         * @param name
106         * @return
107         * @throws IOException
108         */
109        public byte[] getClassBytes(String name) throws IOException
110        {
111            InputStream classStream = null;
112            try
113            {
114                URL classResource = findResource(name.replace('.', '/').concat(".class"));
115                if (classResource == null)
116                {
117                    return null;
118                }
119                classStream = classResource.openStream();
120                return readFully(classStream);
121            }
122            finally
123            {
124                if (classStream != null)
125                {
126                    try
127                    {
128                        classStream.close();
129                    }
130                    catch (IOException e)
131                    {
132                        // Swallow the close exception
133                    }
134                }
135            }
136        }
137    
138        private byte[] runTransformers(String name, byte[] basicClass)
139        {
140            for (IClassTransformer transformer : transformers)
141            {
142                basicClass = transformer.transform(name, basicClass);
143            }
144            return basicClass;
145        }
146    
147        @Override
148        public void addURL(URL url)
149        {
150            super.addURL(url);
151            sources.add(url);
152        }
153    
154        public List<URL> getSources()
155        {
156            return sources;
157        }
158    
159    
160        private byte[] readFully(InputStream stream)
161        {
162            try
163            {
164                ByteArrayOutputStream bos = new ByteArrayOutputStream(stream.available());
165                int r;
166                while ((r = stream.read()) != -1)
167                {
168                    bos.write(r);
169                }
170    
171                return bos.toByteArray();
172            }
173            catch (Throwable t)
174            {
175                /// HMMM
176                return new byte[0];
177            }
178        }
179    
180        public List<IClassTransformer> getTransformers()
181        {
182            return Collections.unmodifiableList(transformers);
183        }
184    }