/* * Copyright 2004-2015 the Seasar Foundation and the Others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.seasar.framework.util; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.util.Enumeration; import java.util.Iterator; import org.seasar.framework.exception.ClassNotFoundRuntimeException; import org.seasar.framework.exception.IORuntimeException; import org.seasar.framework.message.MessageFormatter; /** * {@link ClassLoader}を扱うためのユーティリティ・クラスです。 * * @author koichik */ public abstract class ClassLoaderUtil { private static final Method findLoadedClassMethod = getFindLoadedClassMethod(); private static final Method defineClassMethod = getDefineClassMethod(); private static final Method definePackageMethod = getDefinePackageMethod(); /** * インスタンスを構築します。 */ protected ClassLoaderUtil() { } private static Method getFindLoadedClassMethod() { final Method method = ClassUtil.getDeclaredMethod(ClassLoader.class, "findLoadedClass", new Class[] { String.class }); method.setAccessible(true); return method; } private static Method getDefineClassMethod() { final Method method = ClassUtil.getDeclaredMethod(ClassLoader.class, "defineClass", new Class[] { String.class, byte[].class, int.class, int.class }); method.setAccessible(true); return method; } private static Method getDefinePackageMethod() { final Method method = ClassUtil.getDeclaredMethod(ClassLoader.class, "definePackage", new Class[] { String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class }); method.setAccessible(true); return method; } /** * クラスローダを返します。 * <p> * クラスローダは以下の順で検索します。 * </p> * <ol> * <li>呼び出されたスレッドにコンテキスト・クラスローダが設定されている場合はそのコンテキスト・クラスローダ</li> * <li>ターゲット・クラスをロードしたクラスローダを取得できればそのクラスローダ</li> * <li>このクラスをロードしたクラスローダを取得できればそのクラスローダ</li> * <li>システムを取得できればそのクラスローダ</li> * </ol> * <p> * ただし、ターゲット・クラスをロードしたクラスローダとこのクラスをロードしたクラスローダの両方が取得できた場合で、 * ターゲット・クラスをロードしたクラスローダがこのクラスをロードしたクラスローダの祖先であった場合は、 * このクラスをロードしたクラスローダを返します。 * </p> * * @param targetClass * ターゲット・クラス * @return クラスローダ * @throws IllegalStateException * クラスローダを取得できなかった場合 */ public static ClassLoader getClassLoader(final Class targetClass) { final ClassLoader contextClassLoader = Thread.currentThread() .getContextClassLoader(); if (contextClassLoader != null) { return contextClassLoader; } final ClassLoader targetClassLoader = targetClass.getClassLoader(); final ClassLoader thisClassLoader = ClassLoaderUtil.class .getClassLoader(); if (targetClassLoader != null && thisClassLoader != null) { if (isAncestor(thisClassLoader, targetClassLoader)) { return thisClassLoader; } return targetClassLoader; } if (targetClassLoader != null) { return targetClassLoader; } if (thisClassLoader != null) { return thisClassLoader; } final ClassLoader systemClassLoader = ClassLoader .getSystemClassLoader(); if (systemClassLoader != null) { return systemClassLoader; } throw new IllegalStateException(MessageFormatter.getMessage("ESSR0001", new Object[] { "ClassLoader" })); } /** * コンテキストクラスローダから指定された名前を持つすべてのリソースを探します。 * * @param name * リソース名 * @return リソースに対する URL * オブジェクトの列挙。リソースが見つからなかった場合、列挙は空になる。クラスローダがアクセスを持たないリソースは列挙に入らない * @see java.lang.ClassLoader#getResources(String) */ public static Iterator getResources(final String name) { return getResources(Thread.currentThread().getContextClassLoader(), name); } /** * {@link #getClassLoader(Class)}が返すクラスローダから指定された名前を持つすべてのリソースを探します。 * * @param targetClass * ターゲット・クラス * @param name * リソース名 * @return リソースに対する URL * オブジェクトの列挙。リソースが見つからなかった場合、列挙は空になる。クラスローダがアクセスを持たないリソースは列挙に入らない * @see java.lang.ClassLoader#getResources(String) */ public static Iterator getResources(final Class targetClass, final String name) { return getResources(getClassLoader(targetClass), name); } /** * 指定のクラスローダから指定された名前を持つすべてのリソースを探します。 * * @param loader * クラスローダ * @param name * リソース名 * @return リソースに対する URL * オブジェクトの列挙。リソースが見つからなかった場合、列挙は空になる。クラスローダがアクセスを持たないリソースは列挙に入らない * @see java.lang.ClassLoader#getResources(String) */ public static Iterator getResources(final ClassLoader loader, final String name) { try { final Enumeration e = loader.getResources(name); return new EnumerationIterator(e); } catch (final IOException e) { throw new IORuntimeException(e); } } /** * クラスローダ<code>other</code>がクラスローダ<code>cl</code>の祖先なら<code>true</code>を返します。 * * @param cl * クラスローダ * @param other * クラスローダ * @return クラスローダ<code>other</code>がクラスローダ<code>cl</code>の祖先なら<code>true</code> */ protected static boolean isAncestor(ClassLoader cl, final ClassLoader other) { while (cl != null) { if (cl == other) { return true; } cl = cl.getParent(); } return false; } /** * 指定のクラスローダまたはその祖先の暮らすローダが、 このバイナリ名を持つクラスの起動ローダとしてJava仮想マシンにより記録されていた場合は、 * 指定されたバイナリ名を持つクラスを返します。 記録されていなかった場合は<code>null</code>を返します。 * * @param classLoader * クラスローダ * @param className * クラスのバイナリ名 * @return <code>Class</code>オブジェクト。クラスがロードされていない場合は<code>null</code> * @see java.lang.ClassLoader#findLoadedClass(String) */ public static Class findLoadedClass(final ClassLoader classLoader, final String className) { for (ClassLoader loader = classLoader; loader != null; loader = loader .getParent()) { final Class clazz = (Class) MethodUtil.invoke( findLoadedClassMethod, loader, new Object[] { className }); if (clazz != null) { return clazz; } } return null; } /** * バイトの配列を<code>Class</code>クラスのインスタンスに変換します。 * * @param classLoader * バイナリデータから<code>Class</code>クラスのインスタンスに変換するクラスローダ * @param className * クラスのバイナリ名 * @param bytes * クラスデータを構成するバイト列 * @param offset * クラスデータ<code>bytes</code>の開始オフセット * @param length * クラスデータの長さ * @return 指定されたクラスデータから作成された<code>Class</code>オブジェクト * @see java.lang.ClassLoader#defineClass(String, byte[], int, int) */ public static Class defineClass(final ClassLoader classLoader, final String className, final byte[] bytes, final int offset, final int length) { return (Class) MethodUtil.invoke(defineClassMethod, classLoader, new Object[] { className, bytes, new Integer(offset), new Integer(length) }); } /** * 指定の<code>ClassLoader</code>で名前を使ってパッケージを定義します。 * * @param classLoader * パッケージを定義するクラスローダ * @param name * パッケージ名 * @param specTitle * 仕様のタイトル * @param specVersion * 仕様のバージョン * @param specVendor * 仕様のベンダー * @param implTitle * 実装のタイトル * @param implVersion * 実装のバージョン * @param implVendor * 実装のベンダー * @param sealBase * <code>null</code>でない場合、このパッケージは指定されたコードソース<code>URL</code>オブジェクトを考慮してシールされる。そうでない場合、パッケージはシールされない * @return 新しく定義された<code>Package</code>オブジェクト * @see java.lang.ClassLoader#definePackage(String, String, String, String, * String, String, String, URL) */ public static Package definePackage(final ClassLoader classLoader, final String name, final String specTitle, final String specVersion, final String specVendor, final String implTitle, final String implVersion, final String implVendor, final URL sealBase) { return (Package) MethodUtil.invoke(definePackageMethod, classLoader, new Object[] { name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase }); } /** * 指定されたバイナリ名を持つクラスをロードします。 * * @param loader * クラスローダ * @param className * クラスのバイナリ名 * @return 結果の<code>Class</code>オブジェクト * @throws ClassNotFoundRuntimeException * クラスが見つからなかった場合 * @see java.lang.ClassLoader#loadClass(String) */ public static Class loadClass(final ClassLoader loader, final String className) { try { return loader.loadClass(className); } catch (final ClassNotFoundException e) { throw new ClassNotFoundRuntimeException(e); } } }