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

org.apache.ibatis.javassist.Loader Maven / Gradle / Ivy

The newest version!
/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package org.apache.ibatis.javassist;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Vector;

import org.apache.ibatis.javassist.bytecode.ClassFile;

/**
 * The class loader for Javassist.
 *
 * 

This is a sample class loader using ClassPool. * Unlike a regular class loader, this class loader obtains bytecode * from a ClassPool. * *

Note that Javassist can be used without this class loader; programmers * can define their own versions of class loader. They can run * a program even without any user-defined class loader if that program * is statically translated with Javassist. * This class loader is just provided as a utility class. * *

Suppose that an instance of MyTranslator implementing * the interface Translator is responsible for modifying * class files. * The startup program of an application using MyTranslator * should be something like this: * *

 * import org.apache.ibatis.javassist.*;
 *
 * public class Main {
 *   public static void main(String[] args) throws Throwable {
 *     MyTranslator myTrans = new MyTranslator();
 *     ClassPool cp = ClassPool.getDefault();
 *     Loader cl = new Loader(cp);
 *     cl.addTranslator(cp, myTrans);
 *     cl.run("MyApp", args);
 *   }
 * }
 * 
* *

Class MyApp is the main program of the application. * *

This program should be executed as follows: * *

 * % java Main arg1 arg2...
 * 
* *

It modifies the class MyApp with a MyTranslator * object before the JVM loads it. * Then it calls main() in MyApp with arguments * arg1, arg2, ... * *

This program execution is equivalent to: * *

 * % java MyApp arg1 arg2...
 * 
* *

except that classes are translated by MyTranslator * at load time. * *

If only a particular class must be modified when it is loaded, * the startup program can be simpler; MyTranslator is * unnecessary. For example, if only a class test.Rectangle * is modified, the main() method above will be the following: * *

 * ClassPool cp = ClassPool.getDefault();
 * Loader cl = new Loader(cp);
 * CtClass ct = cp.get("test.Rectangle");
 * ct.setSuperclass(cp.get("test.Point"));
 * cl.run("MyApp", args);
* *

This program changes the super class of the test.Rectangle * class. * *

Note 1: * *

This class loader does not allow the users to intercept the loading * of java.* and javax.* classes (and * sun.*, org.xml.*, ...) unless * Loader.doDelegation is false. This is because * the JVM prohibits a user class loader from loading a system class. * Also see Note 2. * If this behavior is not appropriate, a subclass of Loader * must be defined and loadClassByDelegation() must be overridden. * *

Note 2: * *

If classes are loaded with different class loaders, they belong to * separate name spaces. If class C is loaded by a class * loader CL, all classes that the class C * refers to are also loaded by CL. However, if CL * delegates the loading of the class C to CL', * then those classes that the class C refers to * are loaded by a parent class loader CL' * instead of CL. * *

If an object of class C is assigned * to a variable of class C belonging to a different name * space, then a ClassCastException is thrown. * *

Because of the fact above, this loader delegates only the loading of * org.apache.ibatis.javassist.Loader * and classes included in package java.* and * javax.* to the parent class * loader. Other classes are directly loaded by this loader. * *

For example, suppose that java.lang.String would be loaded * by this loader while java.io.File is loaded by the parent * class loader. If the constructor of java.io.File is called * with an instance of java.lang.String, then it may throw * an exception since it accepts an instance of only the * java.lang.String loaded by the parent class loader. * * @see javassist.ClassPool * @see javassist.Translator */ public class Loader extends ClassLoader { /** * A simpler class loader. * This is a class loader that exposes the protected {@code defineClass()} method * declared in {@code java.lang.ClassLoader}. It provides a method similar to * {@code CtClass#toClass()}. * *

When loading a class, this class loader delegates the work to the * parent class loader unless the loaded classes are explicitly given * by {@link #invokeDefineClass(CtClass)}. * Note that a class {@code Foo} loaded by this class loader is * different from the class with the same name {@code Foo} but loaded by * another class loader. This is Java's naming rule. *

* * @since 3.24 */ public static class Simple extends ClassLoader { /** * Constructs a class loader. */ public Simple() {} /** * Constructs a class loader. * @param parent the parent class loader. */ public Simple(ClassLoader parent) { super(parent); } /** * Invokes the protected {@code defineClass()} in {@code ClassLoader}. * It converts the given {@link CtClass} object into a {@code java.lang.Class} object. */ public Class invokeDefineClass(CtClass cc) throws IOException, CannotCompileException { byte[] code = cc.toBytecode(); return defineClass(cc.getName(), code, 0, code.length); } } private HashMap notDefinedHere; // must be atomic. private Vector notDefinedPackages; // must be atomic. private ClassPool source; private Translator translator; private ProtectionDomain domain; /** * Specifies the algorithm of class loading. * *

This class loader uses the parent class loader for * java.* and javax.* classes. * If this variable doDelegation * is false, this class loader does not delegate those * classes to the parent class loader. * *

The default value is true. */ public boolean doDelegation = true; /** * Creates a new class loader. */ public Loader() { this(null); } /** * Creates a new class loader. * * @param cp the source of class files. */ public Loader(ClassPool cp) { init(cp); } /** * Creates a new class loader * using the specified parent class loader for delegation. * * @param parent the parent class loader. * @param cp the source of class files. */ public Loader(ClassLoader parent, ClassPool cp) { super(parent); init(cp); } private void init(ClassPool cp) { notDefinedHere = new HashMap(); notDefinedPackages = new Vector(); source = cp; translator = null; domain = null; delegateLoadingOf("org.apache.ibatis.javassist.Loader"); } /** * Records a class so that the loading of that class is delegated * to the parent class loader. * *

If the given class name ends with . (dot), then * that name is interpreted as a package name. All the classes * in that package and the sub packages are delegated. */ public void delegateLoadingOf(String classname) { if (classname.endsWith(".")) notDefinedPackages.addElement(classname); else notDefinedHere.put(classname, this); } /** * Sets the protection domain for the classes handled by this class * loader. Without registering an appropriate protection domain, * the program loaded by this loader will not work with a security * manager or a signed jar file. */ public void setDomain(ProtectionDomain d) { domain = d; } /** * Sets the soruce ClassPool. */ public void setClassPool(ClassPool cp) { source = cp; } /** * Adds a translator, which is called whenever a class is loaded. * * @param cp the ClassPool object for obtaining * a class file. * @param t a translator. * @throws NotFoundException if t.start() throws an exception. * @throws CannotCompileException if t.start() throws an exception. */ public void addTranslator(ClassPool cp, Translator t) throws NotFoundException, CannotCompileException { source = cp; translator = t; t.start(cp); } /** * Loads a class with an instance of Loader * and calls main() of that class. * *

This method calls run(). * * @param args command line parameters. *
  {@code args[0]} is the class name to be loaded. *
  {@code args[1..n]} are parameters passed * to the target {@code main()}. * * @see javassist.Loader#run(String[]) */ public static void main(String[] args) throws Throwable { Loader cl = new Loader(); cl.run(args); } /** * Loads a class and calls main() in that class. * * @param args command line parameters. * *
  {@code args[0]} is the class name to be loaded. *
  {@code args[1..n]} are parameters passed * to the target {@code main()}. */ public void run(String[] args) throws Throwable { if (args.length >= 1) run(args[0], Arrays.copyOfRange(args, 1, args.length)); } /** * Loads a class and calls main() in that class. * * @param classname the loaded class. * @param args parameters passed to main(). */ public void run(String classname, String[] args) throws Throwable { Class c = loadClass(classname); try { c.getDeclaredMethod("main", new Class[] { String[].class }).invoke( null, new Object[] { args }); } catch (InvocationTargetException e) { throw e.getTargetException(); } } /** * Requests the class loader to load a class. */ @Override protected Class loadClass(String name, boolean resolve) throws ClassFormatError, ClassNotFoundException { name = name.intern(); synchronized (name) { Class c = findLoadedClass(name); if (c == null) c = loadClassByDelegation(name); if (c == null) c = findClass(name); if (c == null) c = delegateToParent(name); if (resolve) resolveClass(c); return c; } } /** * Finds the specified class using ClassPath. * If the source throws an exception, this returns null. * *

This method can be overridden by a subclass of * Loader. Note that the overridden method must not throw * an exception when it just fails to find a class file. * * @return null if the specified class could not be found. * @throws ClassNotFoundException if an exception is thrown while * obtaining a class file. */ @Override protected Class findClass(String name) throws ClassNotFoundException { byte[] classfile; try { if (source != null) { if (translator != null) translator.onLoad(source, name); try { classfile = source.get(name).toBytecode(); } catch (NotFoundException e) { return null; } } else { String jarname = "/" + name.replace('.', '/') + ".class"; InputStream in = this.getClass().getResourceAsStream(jarname); if (in == null) return null; classfile = ClassPoolTail.readStream(in); } } catch (Exception e) { throw new ClassNotFoundException( "caught an exception while obtaining a class file for " + name, e); } int i = name.lastIndexOf('.'); if (i != -1) { String pname = name.substring(0, i); if (isDefinedPackage(pname)) try { definePackage( pname, null, null, null, null, null, null, null); } catch (IllegalArgumentException e) { // ignore. maybe the package object for the same // name has been created just right away. } } if (domain == null) return defineClass(name, classfile, 0, classfile.length); return defineClass(name, classfile, 0, classfile.length, domain); } private boolean isDefinedPackage(String name) { if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9) return getDefinedPackage(name) == null; else return getPackage(name) == null; } protected Class loadClassByDelegation(String name) throws ClassNotFoundException { /* The swing components must be loaded by a system * class loader. * javax.swing.UIManager loads a (concrete) subclass * of LookAndFeel by a system class loader and cast * an instance of the class to LookAndFeel for * (maybe) a security reason. To avoid failure of * type conversion, LookAndFeel must not be loaded * by this class loader. */ Class c = null; if (doDelegation) if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("jdk.internal.") || name.startsWith("sun.") || name.startsWith("com.sun.") || name.startsWith("org.w3c.") || name.startsWith("org.xml.") || notDelegated(name)) c = delegateToParent(name); return c; } private boolean notDelegated(String name) { if (notDefinedHere.containsKey(name)) return true; for (String pack : notDefinedPackages) if (name.startsWith(pack)) return true; return false; } protected Class delegateToParent(String classname) throws ClassNotFoundException { ClassLoader cl = getParent(); if (cl != null) return cl.loadClass(classname); return findSystemClass(classname); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy