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    }