    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    }