    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    }