All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.groupbyinc.common.jackson.module.afterburner.util.MyClassLoader Maven / Gradle / Ivy

There is a newer version: 198
Show newest version
package com.fasterxml.jackson.module.afterburner.util;

import java.lang.reflect.Method;
import java.nio.charset.Charset;

/**
 * Class loader that is needed to load generated classes.
 */
public class MyClassLoader extends ClassLoader
{
    private final static Charset UTF8 = Charset.forName("UTF-8");

    /**
     * Flag that determines if we should first try to load new class
     * using parent class loader or not; this may be done to try to
     * force access to protected/package-access properties.
     */
    protected final boolean _cfgUseParentLoader;
    
    public MyClassLoader(ClassLoader parent, boolean tryToUseParent)
    {
        super(parent);
        _cfgUseParentLoader = tryToUseParent;
    }

    /**
     * Helper method called to check whether it is acceptable to create a new
     * class in package that given class is part of.
     * This is used to prevent certain class of failures, related to access
     * limitations: for example, we can not add classes in sealed packages,
     * or core Java packages (java.*).
     * 
     * @since 2.2.1
     */
    public static boolean canAddClassInPackageOf(Class cls)
    {
        final Package beanPackage = cls.getPackage();
        if (beanPackage != null) {
            if (beanPackage.isSealed()) {
                return false;
            }
            String pname = beanPackage.getName();
            /* 14-Aug-2014, tatu: java.* we do not want to touch, but
             *    javax is bit trickier. For now let's 
             */
            if (pname.startsWith("java.")
                    || pname.startsWith("javax.security.")) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * @param className Interface or abstract class that class to load should extend or 
     *   implement
     */
    public Class loadAndResolve(ClassName className, byte[] byteCode)
        throws IllegalArgumentException
    {
        // First things first: just to be sure; maybe we have already loaded it?
        Class old = findLoadedClass(className.getDottedName());
        if (old != null) {
            return old;
        }
        
        Class impl;

        // Important: bytecode is generated with a template name (since bytecode itself
        // is used for checksum calculation) -- must be replaced now, however
        replaceName(byteCode, className.getSlashedTemplate(), className.getSlashedName());
        
        // First: let's try calling it directly on parent, to be able to access protected/package-access stuff:
        if (_cfgUseParentLoader) {
            ClassLoader cl = getParent();
            // if we have parent, that is
            if (cl != null) {
                try {
                    Method method = ClassLoader.class.getDeclaredMethod("defineClass",
                            new Class[] {String.class, byte[].class, int.class,
                            int.class});
                    method.setAccessible(true);
                    return (Class)method.invoke(getParent(),
                            className.getDottedName(), byteCode, 0, byteCode.length);
                } catch (Exception e) {
                    // Should we handle this somehow?
                }
            }
        }

        // but if that doesn't fly, try to do it from our own class loader
    
        try {
            impl = defineClass(className.getDottedName(), byteCode, 0, byteCode.length);
        } catch (LinkageError e) {
            Throwable t = e;
            while (t.getCause() != null) {
                t = t.getCause();
            }
            throw new IllegalArgumentException("Failed to load class '"+className+"': "+t.getMessage(), t);
        }
        // important: must also resolve the class...
        resolveClass(impl);
        return impl;
    }
    
    public static int replaceName(byte[] byteCode,
            String from, String to)
    {
        byte[] fromB = from.getBytes(UTF8);
        byte[] toB = to.getBytes(UTF8);

        final int matchLength = fromB.length;

        // sanity check
        if (matchLength != toB.length) {
            throw new IllegalArgumentException("From String '"+from
                    +"' has different length than To String '"+to+"'");
        }

        int i = 0;
        int count = 0;

        // naive; for now has to do
        main_loop:
        for (int end = byteCode.length - matchLength; i <= end; ) {
            if (byteCode[i++] == fromB[0]) {
                for (int j = 1; j < matchLength; ++j) {
                    if (fromB[j] != byteCode[i+j-1]) {
                        continue main_loop;
                    }
                }
                ++count;
                System.arraycopy(toB, 0, byteCode, i-1, matchLength);
                i += (matchLength-1);
            }
        }
        return count;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy