me.rgcjonas.portableMinecraftLauncher.ModdingClassLoader.java Source code

Java tutorial

Introduction

Here is the source code for me.rgcjonas.portableMinecraftLauncher.ModdingClassLoader.java

Source

/*
 *   Copyright (C) 2012 Jonas Kuemmerlin <rgcjonas@gmail.com>
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package me.rgcjonas.portableMinecraftLauncher;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.cert.Certificate;
import java.security.CodeSource;
import java.util.HashMap;

import org.apache.commons.io.IOUtils;

import javassist.ClassPool;
import javassist.LoaderClassPath;

/**
 * A ClassLoader designed for modifying Class files at runtime while loading them.
 * 
 * In order to use this, you need to subclass and override {@link getModdedClasses}. 
 * The resulting class can be used as drop-in replacement for URLClassLoader.
 * 
 * This loader was designed to be memory-efficient. To achieve that, all to be modified classes are 
 * modified at once so the whole ClassPool and other javassist stuff can be disposed by the GC.
 * 
 * The whole modding takes place while the loader is constructed. 
 * URLs added later can't be modified.
 * 
 * Note that the loader generates CodeSources, 
 * but certificates/CodeSigners are dropped (how can you access them?)
 * 
 * @author Jonas Kuemmerlin <rgcjonas@gmail.com>
 *
 */
public abstract class ModdingClassLoader extends URLClassLoader {
    private HashMap<String, byte[]> moddedClasses;

    public ModdingClassLoader(URL[] urls) {
        super(urls);

        ClassPool pool = new ClassPool();
        pool.appendSystemPath();
        pool.appendClassPath(new LoaderClassPath(this));

        this.moddedClasses = getModdedClasses(pool);
    }

    /**
     * This method needs to be overridden. Modify the classes inside this function.
     * 
     * @param pool The ClassPool with all available classes
     * @return a HashMap with the class names as keys and a byte array containing the class
     */
    abstract public HashMap<String, byte[]> getModdedClasses(ClassPool pool);

    public Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = null;
        String resourceName = name.replace('.', '/').concat(".class"); //getResource expects this form
        if (moddedClasses.containsKey(name)) {
            data = moddedClasses.get(name);
        } else {
            try {
                InputStream is = this.getResourceAsStream(resourceName);
                if (is == null) {
                    System.out.println("class not found: " + resourceName + "\n");
                    throw new ClassNotFoundException();
                }
                data = IOUtils.toByteArray(is);
            } catch (IOException e) {
                throw new ClassNotFoundException(e.toString());
            }
        }
        CodeSource source = new CodeSource(this.getResource(resourceName), new Certificate[] {});
        return defineClass(name, data, 0, data.length, source);
    }

}