/* * Copyright 2004-2012 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.util.lang; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.util.Enumeration; import java.util.Iterator; import org.seasar.util.collection.EnumerationIterator; import org.seasar.util.exception.ClassNotFoundRuntimeException; import org.seasar.util.exception.IORuntimeException; import org.seasar.util.exception.SIllegalStateException; import org.seasar.util.message.MessageFormatter; import static org.seasar.util.lang.ClassLoaderIterator.*; import static org.seasar.util.misc.AssertionUtil.*; /** * {@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(); private static Method getFindLoadedClassMethod() { final Method method = ClassUtil.getDeclaredMethod( ClassLoader.class, "findLoadedClass", String.class); method.setAccessible(true); return method; } private static Method getDefineClassMethod() { final Method method = ClassUtil.getDeclaredMethod( ClassLoader.class, "defineClass", 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", 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 * ターゲット・クラス。{@literal null}であってはいけません * @return クラスローダ * @throws IllegalStateException * クラスローダを取得できなかった場合 */ public static ClassLoader getClassLoader(final Class<?> targetClass) { assertArgumentNotNull("targetClass", 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 SIllegalStateException(MessageFormatter.getMessage( "EUTL0001", "ClassLoader")); } /** * クラスローダ<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) { for (final ClassLoader loader : iterable(cl)) { if (loader == other) { return true; } } return false; } /** * コンテキストクラスローダから指定された名前を持つすべてのリソースを探します。 * * @param name * リソース名。{@literal null}や空文字列であってはいけません * @return リソースに対する URL * オブジェクトの列挙。リソースが見つからなかった場合、列挙は空になる。クラスローダがアクセスを持たないリソースは列挙に入らない * @see java.lang.ClassLoader#getResources(String) */ public static Iterator<URL> getResources(final String name) { assertArgumentNotEmpty("name", name); return getResources( Thread.currentThread().getContextClassLoader(), name); } /** * {@link #getClassLoader(Class)}が返すクラスローダから指定された名前を持つすべてのリソースを探します。 * * @param targetClass * ターゲット・クラス。{@literal null}であってはいけません * @param name * リソース名。{@literal null}や空文字列であってはいけません * @return リソースに対する URL * オブジェクトの列挙。リソースが見つからなかった場合、列挙は空になる。クラスローダがアクセスを持たないリソースは列挙に入らない * @see java.lang.ClassLoader#getResources(String) */ public static Iterator<URL> getResources(final Class<?> targetClass, final String name) { assertArgumentNotNull("targetClass", targetClass); assertArgumentNotNull("name", name); return getResources(getClassLoader(targetClass), name); } /** * 指定のクラスローダから指定された名前を持つすべてのリソースを探します。 * * @param loader * クラスローダ。{@literal null}であってはいけません * @param name * リソース名。{@literal null}や空文字列であってはいけません * @return リソースに対する URL * オブジェクトの列挙。リソースが見つからなかった場合、列挙は空になる。クラスローダがアクセスを持たないリソースは列挙に入らない * @see java.lang.ClassLoader#getResources(String) */ public static Iterator<URL> getResources(final ClassLoader loader, final String name) { assertArgumentNotNull("loader", loader); assertArgumentNotEmpty("name", name); try { final Enumeration<URL> e = loader.getResources(name); return new EnumerationIterator<URL>(e); } catch (final IOException e) { throw new IORuntimeException(e); } } /** * 指定のクラスローダまたはその祖先のクラスローダが、 このバイナリ名を持つクラスの起動ローダとしてJava仮想マシンにより記録されていた場合は、 * 指定されたバイナリ名を持つクラスを返します。 記録されていなかった場合は<code>null</code>を返します。 * * @param classLoader * クラスローダ。{@literal null}であってはいけません * @param className * クラスのバイナリ名。{@literal null}や空文字列であってはいけません * @return <code>Class</code>オブジェクト。クラスがロードされていない場合は<code>null</code> * @see java.lang.ClassLoader#findLoadedClass(String) */ public static Class<?> findLoadedClass(final ClassLoader classLoader, final String className) { assertArgumentNotNull("classLoader", classLoader); assertArgumentNotEmpty("className", className); for (final ClassLoader loader : iterable(classLoader)) { final Class<?> clazz = (Class<?>) MethodUtil.invoke( findLoadedClassMethod, loader, className); if (clazz != null) { return clazz; } } return null; } /** * バイトの配列を<code>Class</code>クラスのインスタンスに変換します。 * * @param classLoader * バイナリデータから<code>Class</code>クラスのインスタンスに変換するクラスローダ。 * {@literal null}であってはいけません * @param className * クラスのバイナリ名。{@literal null}や空文字列であってはいけません * @param bytes * クラスデータを構成するバイト列。{@literal null}や空配列であってはいけません * @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) { assertArgumentNotNull("classLoader", classLoader); assertArgumentNotEmpty("className", className); assertArgumentNotEmpty("bytes", bytes); return (Class<?>) MethodUtil.invoke( defineClassMethod, classLoader, className, bytes, offset, length); } /** * 指定の<code>ClassLoader</code>で名前を使ってパッケージを定義します。 * * @param classLoader * パッケージを定義するクラスローダ。{@literal null}であってはいけません * @param name * パッケージ名。{@literal null}や空文字列であってはいけません * @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) { assertArgumentNotNull("classLoader", classLoader); assertArgumentNotEmpty("name", name); return (Package) MethodUtil.invoke( definePackageMethod, classLoader, name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); } /** * 指定されたバイナリ名を持つクラスをロードします。 * * @param loader * クラスローダ。{@literal null}であってはいけません * @param className * クラスのバイナリ名。{@literal null}や空文字列であってはいけません * @return 結果の<code>Class</code>オブジェクト * @throws ClassNotFoundRuntimeException * クラスが見つからなかった場合 * @see java.lang.ClassLoader#loadClass(String) */ public static Class<?> loadClass(final ClassLoader loader, final String className) { assertArgumentNotNull("loader", loader); assertArgumentNotEmpty("className", className); try { return loader.loadClass(className); } catch (final ClassNotFoundException e) { throw new ClassNotFoundRuntimeException(e); } } }