org.spoutcraft.launcher.launch.MinecraftClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.spoutcraft.launcher.launch.MinecraftClassLoader.java

Source

/*
 * This file is part of Spoutcraft.
 *
 * Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
 * Spoutcraft is licensed under the SpoutDev License Version 1.
 *
 * Spoutcraft is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the SpoutDev License Version 1.
 *
 * Spoutcraft is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License,
 * the MIT license and the SpoutDev License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license,
 * including the MIT license.
 */
package org.spoutcraft.launcher.launch;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.ClosedByInterruptException;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipException;

import org.apache.commons.io.FileUtils;

import org.spoutcraft.launcher.api.SpoutcraftDirectories;
import org.spoutcraft.launcher.util.Utils;

public class MinecraftClassLoader extends URLClassLoader {
    private HashMap<String, Class<?>> loadedClasses = new HashMap<String, Class<?>>(10000);
    private HashSet<String> preloaded = new HashSet<String>();
    private HashMap<String, File> classLocations = new HashMap<String, File>(10000);

    public MinecraftClassLoader(ClassLoader parent, File spoutcraft, File[] libraries) {
        super(new URL[0], parent);

        // Move all of the jars we want to use to a temp folder (so we don't create file hooks on them)
        File tempDir = getTempDirectory();
        for (File f : libraries) {
            try {
                File replacement = new File(tempDir, f.getName());
                FileUtils.copyFile(f, replacement);
                this.addURL(replacement.toURI().toURL());
                index(replacement);
            } catch (ClosedByInterruptException e) {
                // Ignore, assume we interrupted for a reason
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            File tempSpoutcraft = new File(tempDir, spoutcraft.getName());
            FileUtils.copyFile(spoutcraft, tempSpoutcraft);
            spoutcraft = tempSpoutcraft;
            this.addURL(spoutcraft.toURI().toURL());
            index(spoutcraft);
        } catch (ClosedByInterruptException e) {
            // Ignore, assume we interrupted for a reason
            return;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private File getTempDirectory() {
        SpoutcraftDirectories dir = new SpoutcraftDirectories();
        int index = 0;
        while (true) {
            File tempDir = new File(dir.getBinDir(), "temp_" + index);
            if (!tempDir.isDirectory() && !tempDir.exists()) {
                tempDir.mkdirs();
                return tempDir;
            }
            index++;
        }
    }

    private void index(File file) throws IOException {
        JarFile jar = null;
        try {
            jar = new JarFile(file);
            Enumeration<JarEntry> i = jar.entries();
            while (i.hasMoreElements()) {
                JarEntry entry = i.nextElement();
                if (entry.getName().endsWith(".class")) {
                    String name = entry.getName();
                    name = name.replace("/", ".").substring(0, name.length() - 6);
                    classLocations.put(name, file);
                }
            }
        } catch (IOException e) {
            throw e;
        } finally {
            if (jar != null) {
                try {
                    jar.close();
                } catch (IOException ignore) {
                }
            }
        }
    }

    public int preloadClasses(int amount) {
        Iterator<Entry<String, File>> i = classLocations.entrySet().iterator();
        int preloaded = 0;
        while (i.hasNext() && preloaded < amount) {

            if (Thread.currentThread().isInterrupted()) {
                break;
            }

            Entry<String, File> entry = i.next();
            String className = entry.getKey();
            if (!this.preloaded.contains(className)) {
                try {
                    this.loadClass(className);
                    preloaded++;
                } catch (Throwable ignore) {
                }
            }
            this.preloaded.add(className);
        }
        return preloaded;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> result = null;
        result = loadedClasses.get(name); // Checks in cached classes
        if (result != null) {
            return result;
        }

        File f = classLocations.get(name);
        if (f != null) {
            result = findClassInjar(name, f);
            if (result != null) {
                return result;
            }
        }

        return super.findClass(name);
    }

    private Class<?> findClassInjar(String name, File file) throws ClassNotFoundException {
        byte classByte[];
        Class<?> result = null;
        JarFile jar = null;
        try {
            jar = new JarFile(file);
            JarEntry entry = jar.getJarEntry(name.replace(".", "/") + ".class");
            if (entry != null) {
                InputStream is = jar.getInputStream(entry);
                ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
                int next = is.read();
                while (-1 != next) {
                    byteStream.write(next);
                    next = is.read();
                }

                classByte = byteStream.toByteArray();
                result = defineClass(name, classByte, 0, classByte.length,
                        new CodeSource(file.toURI().toURL(), (CodeSigner[]) null));
                loadedClasses.put(name, result);
                return result;
            }
        } catch (FileNotFoundException e) {
            // Assume temp file has been cleaned if the thread is interrupted
            if (!Thread.currentThread().isInterrupted()) {
                e.printStackTrace();
            }
        } catch (ZipException zipEx) {
            System.out.println("Failed to open " + name + " from " + file.getPath());
            zipEx.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                jar.close();
            } catch (IOException ignore) {
            }
        }
        return null;
    }

    Map<String, byte[]> pngResource = new HashMap<String, byte[]>();
    Map<String, List<URL>> resources = new HashMap<String, List<URL>>();

    @Override
    public InputStream getResourceAsStream(String resource) {
        URL result = getResource(resource);
        if (result != null) {
            try {
                return result.openStream();
            } catch (IOException e) {
                //e.printStackTrace();
            }
        }
        return super.getResourceAsStream(resource);
    }

    @Override
    public URL getResource(String resource) {
        Enumeration<URL> results;
        try {
            results = getResources(resource);
            while (results.hasMoreElements()) {
                return results.nextElement();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.getResource(resource);
    }

    @Override
    public Enumeration<URL> getResources(String resource) throws IOException {
        if (resource != null) {
            if (resources.containsKey(resource)) {
                return new IteratorEnumerator(resources.get(resource).iterator());
            }
            Enumeration<URL> result = null;
            if (resource.startsWith("res/")) {
                result = getEnumeration(Utils.getAssetsDirectory().getCanonicalPath() + resource.substring(3),
                        resource);
            } else if (resource.startsWith("/res/")) {
                result = getEnumeration(Utils.getAssetsDirectory().getCanonicalPath() + resource.substring(4),
                        resource);
            }

            if (result != null) {
                return result;
            }
        }
        return super.getResources(resource);
    }

    private Enumeration<URL> getEnumeration(String resource, String key) throws MalformedURLException {
        ArrayList<URL> list = new ArrayList<URL>(1);
        File file = new File(resource);
        if (file.exists()) {
            try {
                list.add(file.getCanonicalFile().toURI().toURL());
            } catch (IOException e) {
                list.add(file.toURI().toURL());
            }
            resources.put(key, list);
            return new IteratorEnumerator(list.iterator());
        }
        return null;
    }

    private class IteratorEnumerator implements Enumeration<URL> {
        final Iterator<URL> iterator;

        protected IteratorEnumerator(Iterator<URL> iterator) {
            this.iterator = iterator;
        }

        public boolean hasMoreElements() {
            return iterator.hasNext();
        }

        public URL nextElement() {
            return iterator.next();
        }
    }
}