com.feilong.core.lang.ClassLoaderUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.core.lang.ClassLoaderUtil.java

Source

/*
 * Copyright (C) 2008 feilong
 *
 * 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 com.feilong.core.lang;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.feilong.core.UncheckedIOException;
import com.feilong.tools.jsonlib.JsonUtil;
import com.feilong.tools.slf4j.Slf4jUtil;

import static com.feilong.core.bean.ConvertUtil.toList;

/**
 * {@link java.lang.ClassLoader ClassLoader}.
 * 
 * <h3>?:</h3>
 * 
 * <blockquote>
 * 
 * <ul>
 * <li>{@link #getResource(String)}</li>
 * <li>{@link #getResourceInAllClassLoader(String, Class)}</li>
 * </ul>
 * </blockquote>
 * 
 * <h3> {@link java.lang.Class#getResourceAsStream(String) Class#getResourceAsStream(String)} VS
 * {@link java.lang.ClassLoader#getResourceAsStream(String) ClassLoader#getResourceAsStream(String)}</h3>
 * 
 * <blockquote>
 * <p>
 * ,? classpath ???,classpath?classpathclasspathjar
 * </p>
 * <p>
 * ?? src/main/resources?, messages/feilong-core-message_en_US.properties,
 * </p>
 * <ol>
 * <li>{@link java.lang.Class#getResourceAsStream(String) Class#getResourceAsStream(String)} ?
 * <b>"/messages/feilong-core-message_en_US.properties"</b>,<br>
 * ???;  / ,?, ? / , class</li>
 * <li>{@link java.lang.ClassLoader#getResourceAsStream(String) ClassLoader#getResourceAsStream(String)} ?
 * <b>"messages/feilong-core-message_en_US.properties"</b>,<br>
 * {@link ClassLoader} JVMBootstrapLoader?.<br>
 * ??"messages/feilong-core-message_en_US.properties" <span style="color:red">??"/"</span></li>
 * <li>spring,???, <code>org.springframework.core.io.ClassPathResource</code>,{@link ClassLoader},?
 * <code>org.springframework.util.StringUtils#cleanPath(String)</code>,?/?, ,?</li>
 * </ol>
 * 
 * </blockquote>
 *
 * @author <a href="http://feitianbenyue.iteye.com/">feilong</a>
 * @see java.lang.ClassLoader
 * @see java.net.URLClassLoader
 * @see "org.springframework.core.io.ClassPathResource#ClassPathResource(String, ClassLoader)"
 * @since 1.0.0
 */
public final class ClassLoaderUtil {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(ClassLoaderUtil.class);

    /** Don't let anyone instantiate this class. */
    private ClassLoaderUtil() {
        //AssertionError?. ?????. ???.
        //see Effective Java 2nd
        throw new AssertionError("No " + getClass().getName() + " instances for you!");
    }

    /**
     * ???,?????????.
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <p>
     * ,<code> src/test/resources</code> ? <code>messages/feilong-core-test.properties</code> ?,?,?
     * <code>target/test-classes/messages/feilong-core-test.properties</code>
     * </p>
     * 
     * <pre class="code">
     * ClassLoaderUtil.getResource("/messages/feilong-core-test.properties") 
     *  ClassLoaderUtil.getResource("messages/feilong-core-test.properties")
     * </pre>
     * 
     * ?
     * 
     * <pre class="code">
     * file:/E:/Workspaces/feilong/feilong-core/target/test-classes/messages/feilong-core-test.properties
     * </pre>
     * 
     * </blockquote>
     * 
     * <h3>?:</h3>
     * <blockquote>
     * <ol>
     * <li> <code>resourceName</code> ? "/" ,?, ClassLoader?????, ??
     * <code>org.springframework.core.io.ClassPathResource#ClassPathResource(String, ClassLoader)</code></li>
     * <li>"",classes </li>
     * </ol>
     * </blockquote>
     * 
     * <h3>???:</h3>
     * 
     * <blockquote>
     * <table border="1" cellspacing="0" cellpadding="4" summary="">
     * <tr style="background-color:#ccccff">
     * <th align="left"></th>
     * <th align="left">(maven)</th>
     * <th align="left">web,(??jar)</th>
     * </tr>
     * <tr valign="top">
     * <td><code>getResource("")</code></td>
     * <td>file:/E:/Workspaces/feilong/feilong-platform/feilong-core/target/test-classes/</td>
     * <td>file:/E:/Workspaces/feilong/feilong-platform/feilong-web-test/src/main/webapp/WEB-INF/classes/</td>
     * </tr>
     * <tr valign="top" style="background-color:#eeeeff">
     * <td><code>getResource("com")</code></td>
     * <td>file:/E:/Workspaces/feilong/feilong-platform/feilong-core/target/test-classes/com</td>
     * <td>file:/E:/Workspaces/feilong/feilong-platform/feilong-web-test/src/main/webapp/WEB-INF/classes/com/</td>
     * </tr>
     * </table>
     * </blockquote>
     *
     * @param resourceName
     *            the resource name
     * @return  <code>resourceName</code> null, {@link NullPointerException}<br>
     *         ??,????, null
     * @see org.apache.commons.lang3.ClassPathUtils#toFullyQualifiedPath(Package, String)
     * @see #getResource(ClassLoader, String)
     * @see #getClassLoaderByClass(Class)
     */
    public static URL getResource(String resourceName) {
        return getResource(getClassLoaderByClass(ClassLoaderUtil.class), resourceName);
    }

    /**
     * ???,?????????.
     * 
     * <h3>?:</h3>
     * <blockquote>
     * <ol>
     * <li> <code>resourceName</code>  ? "/" ,?,  ClassLoader???? ?, ??
     * <code>org.springframework.core.io.ClassPathResource#ClassPathResource(String, ClassLoader)</code></li>
     * <li>"",classes </li>
     * </ol>
     * </blockquote>
     *
     * @param classLoader
     *            the class loader
     * @param resourceName
     *            the resource name
     * @return  <code>classLoader</code> null, {@link NullPointerException}<br>
     *          <code>resourceName</code> null, {@link NullPointerException}<br>
     *         ??,????, null
     * @since 1.2.1
     */
    private static URL getResource(ClassLoader classLoader, String resourceName) {
        Validate.notNull(classLoader, "classLoader can't be null!");
        Validate.notNull(resourceName, "resourceName can't be null!");

        boolean startsWithSlash = resourceName.startsWith("/");
        String usePath = startsWithSlash ? StringUtil.substring(resourceName, 1) : resourceName;
        URL result = classLoader.getResource(usePath);

        LOGGER.info("search resource:[\"{}\"] in [{}],result:[{}]", resourceName, classLoader, result);
        return result;
    }

    // *****************************************************
    /**
     * This is a convenience method to load a resource as a stream.
     * 
     * <h3>?:</h3>
     * <blockquote>
     * <ol>
     * <li> <code>resourceName</code>  ? "/" ,?,  ClassLoader???? ?, ??
     * <code>org.springframework.core.io.ClassPathResource#ClassPathResource(String, ClassLoader)</code></li>
     * <li>"",classes </li>
     * </ol>
     * </blockquote>
     * 
     * @param resourceName
     *            The name of the resource to load
     * @param callingClass
     *            The Class object of the calling object
     * @return  <code>resourceName</code> null, {@link NullPointerException}<br>
     *         ??, null
     * @see #getResourceInAllClassLoader(String, Class)
     * @see "org.apache.velocity.util.ClassUtils#getResourceAsStream(Class, String)"
     */
    public static InputStream getResourceAsStream(String resourceName, Class<?> callingClass) {
        URL url = getResourceInAllClassLoader(resourceName, callingClass);
        try {
            return url == null ? null : url.openStream();
        } catch (IOException e) {
            String message = Slf4jUtil.format("can not open resourceName:[{}]", resourceName);
            LOGGER.error(message, e);
            throw new UncheckedIOException(message, e);
        }
    }

    /**
     * Load a given resource.
     * 
     * <h3>?:</h3>
     * <blockquote>
     * <ol>
     * <li> <code>resourceName</code>  ? "/" ,?,  ClassLoader???? ?, ??
     * <code>org.springframework.core.io.ClassPathResource#ClassPathResource(String, ClassLoader)</code></li>
     * <li>"",classes </li>
     * </ol>
     * </blockquote>
     * 
     * <p>
     * This method will try to load the resource using the following methods (in order):
     * </p>
     * <ul>
     * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
     * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
     * <li>From {@link Class#getClassLoader() callingClass.getClassLoader() }
     * </ul>
     * 
     * @param resourceName
     *            The name of the resource to load
     * @param callingClass
     *            The Class object of the calling object
     * @return  <code>resourceName</code> null, {@link NullPointerException}<br>
     *         {@link ClassLoader}???,null
     * @since 1.6.2
     */
    public static URL getResourceInAllClassLoader(String resourceName, Class<?> callingClass) {
        List<ClassLoader> classLoaderList = getAllClassLoaderList(callingClass);
        for (ClassLoader classLoader : classLoaderList) {
            if (null == classLoader) {
                continue;
            }
            URL url = getResource(classLoader, resourceName);
            if (null == url) {
                LOGGER.warn(getLogInfo(resourceName, classLoader, false));
            } else {
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace(getLogInfo(resourceName, classLoader, true));
                }
                return url;
            }
        }
        LOGGER.warn("not found:[{}] in all ClassLoader,return null", resourceName);
        return null;
    }

    //**************************************************************************************************

    /**
     *  all class loader list.
     *
     * @param callingClass
     *            the calling class
     * @return the all class loader
     * @since 1.6.2
     */
    private static List<ClassLoader> getAllClassLoaderList(Class<?> callingClass) {
        return toList(getClassLoaderByCurrentThread(), getClassLoaderByClass(ClassLoaderUtil.class),
                null == callingClass ? null : getClassLoaderByClass(callingClass));
    }

    /**
     *  {@link Thread#getContextClassLoader()}  {@link ClassLoader}.
     * 
     * @return the class loader by current thread
     */
    private static ClassLoader getClassLoaderByCurrentThread() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("[Thread.currentThread()].getContextClassLoader:{}", formatClassLoader(classLoader));
        }
        return classLoader;
    }

    /**
     * ? {@link ClassLoader}.
     * 
     * @param callingClass
     *            the calling class
     * @return  <code>callingClass</code> null, {@link NullPointerException}<br>
     * @see java.lang.Class#getClassLoader()
     */
    private static ClassLoader getClassLoaderByClass(Class<?> callingClass) {
        Validate.notNull(callingClass, "callingClass can't be null!");
        ClassLoader classLoader = callingClass.getClassLoader();
        if (LOGGER.isTraceEnabled()) {
            LOGGER.trace("[{}].getClassLoader():{}", callingClass.getSimpleName(), formatClassLoader(classLoader));
        }
        return classLoader;
    }

    /**
     *  log info.
     *
     * @param resourceName
     *            the resource name
     * @param classLoader
     *            the class loader
     * @param isFouned
     *            the is founed
     * @return the log info
     * @since 1.6.2
     */
    private static String getLogInfo(String resourceName, ClassLoader classLoader, boolean isFouned) {
        String message = "{}found [{}],in ClassLoader:[{}]";
        return Slf4jUtil.format(message, isFouned ? "" : "not ", resourceName, formatClassLoader(classLoader));
    }

    /**
     * Format class loader.
     *
     * @param classLoader
     *            the class loader
     * @return the string
     * @since 1.6.2
     */
    private static String formatClassLoader(ClassLoader classLoader) {
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        map.put("classLoader", "" + classLoader);
        map.put("classLoader[CanonicalName]", classLoader.getClass().getCanonicalName());
        map.put("classLoader[Root Classpath]", "" + getRootClassPath(classLoader));
        return JsonUtil.format(map);
    }

    /**
     *  class path.
     *
     * @param classLoader
     *            the class loader
     * @return  <code>classLoader</code> null, {@link NullPointerException}<br>
     * @see #getResource(ClassLoader, String)
     * @since 1.6.2
     */
    private static URL getRootClassPath(ClassLoader classLoader) {
        Validate.notNull(classLoader, "classLoader can't be null!");
        return getResource(classLoader, "");
    }

}