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

org.openmdx.kernel.loading.Classes Maven / Gradle / Ivy

There is a newer version: 2.18.10
Show newest version
/*
 * ====================================================================
 * Project:     openMDX, http://www.openmdx.org/
 * Description: Application Framework: Classes 
 * Owner:       OMEX AG, Switzerland, http://www.omex.ch
 * ====================================================================
 *
 * This software is published under the BSD license as listed below.
 * 
 * Copyright (c) 2004-2017, OMEX AG, Switzerland
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 * 
 * * Neither the name of the openMDX team nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * ------------------
 * 
 * This product includes software developed by other organizations as
 * listed in the NOTICE file.
 */
package org.openmdx.kernel.loading;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.openmdx.kernel.exception.BasicException;
import org.openmdx.kernel.exception.Throwables;
import org.openmdx.kernel.platform.Platform;

/**
 * Generic class loader access
 */
public class Classes { 

    /**
     * Constructor 
     */
    private Classes(
    ){
        // Avoid instantiation
    }

    /**
     * Maps primitive types to their corresponding object class
     */
    private static final Map, Class> PRIMITIVE_TYPE_TO_OBJECT_CLASS = new HashMap, Class>();

    static {
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Boolean.TYPE, Boolean.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Character.TYPE, Character.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Byte.TYPE, Byte.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Short.TYPE, Short.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Integer.TYPE, Integer.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Long.TYPE, Long.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Float.TYPE, Float.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Double.TYPE, Double.class);
    	PRIMITIVE_TYPE_TO_OBJECT_CLASS.put(Void.TYPE, Void.class);
    }

    /**
     * Convert primitive types to their respective object class and
     * leave object classes as they are.
     * 
     * @param type a primitive type or an object class
     * 
     * @return the corresponding object class in case of a primitive type and 
     * the type itself otherwise
     */
    public static Class toObjectClass(Class type){
    	return type.isPrimitive() ? PRIMITIVE_TYPE_TO_OBJECT_CLASS.get(type) : type;
    }
    
    
    //------------------------------------------------------------------------
    // Class loading
    //------------------------------------------------------------------------
    
    /**
     * Retrieve the standard class loader
     * 
     * @return the standard class loader
     */
    private static final ClassLoader getStandardClassLoader(
    ){
        switch(ClassLoaderType.getStandardClassLoaderType()) {
            case CONTEXT_CLASS_LOADER:
                return Thread.currentThread().getContextClassLoader();
            case KERNEL_CLASS_LOADER:    
                return Classes.class.getClassLoader();
            default: 
                throw new RuntimeException("Unsupported class loader type: " + ClassLoaderType.getStandardClassLoaderType());
        }
    }

    /**
     * Retrieve information about the the class loaders failing to provide the given class
     * 
     * @param type The entity type
     * @param name The entity name
     * @param classLoader
     *  
     * @return BasicException.Parameters
     */
    private static final BasicException.Parameter[] getInfo(
        String type,
        String name, 
        ClassLoader classLoader
    ){
        List info = new ArrayList();
        info.add(new BasicException.Parameter(type, name));
        try {
            int i = 0;
            for(
                ClassLoader current = classLoader;
                current != null;
                current = current.getParent(), i++
            ){
                info.add(new BasicException.Parameter("classLoader[" + i + "]", current.getClass().getName()));
                if(current instanceof URLClassLoader) {
                    int j = 0;
                    for(URL url : ((URLClassLoader)current).getURLs()) {
                        info.add(new BasicException.Parameter("url[" + i + "," + j++ + "]", url));
                    }
                }
            }
        } catch (RuntimeException ignore) {
            // just end info generation
        }
        return info.toArray(
            new BasicException.Parameter[info.size()]
        );
    }
    
    /**
     * This method may be overridden by a specific Classes instance.
     *
     * @param     name
     *            fully qualified name of the desired class
     * @param     classLoader
     *            the classLoader ot be used           
     *
     * @exception LinkageError
     *            if the linkage fails 
     * @exception ExceptionInInitializerError
     *            if the initialization provoked by this method fails 
     * @exception ClassNotFoundException
     *            if the class cannot be located by the kernel class loader
     */
    @SuppressWarnings("unchecked")
    private static  Class  getClass(
        String name,
        ClassLoader classLoader
    ) throws ClassNotFoundException {
    	try {
	        return (Class) Class.forName(
	            name,
	            true,
				classLoader
	        );
    	} catch (NoClassDefFoundError error) {
    	    throw Throwables.initCause(
    	        new NoClassDefFoundError(
        			"Could not load " + name + 
        			"; maybe it depends on another class which is " +
        			"either missing or to be found in a child class loader"
        		),
        		error,
        		BasicException.Code.DEFAULT_DOMAIN,
        		BasicException.Code.INITIALIZATION_FAILURE,
        		getInfo(
        		    "class",
        		    name, 
        		    classLoader
        		)
    		);
    	} catch (ClassNotFoundException exception) {
            throw BasicException.initHolder(
                new ClassNotFoundException(
                    "Could not load " + name,
                    BasicException.newEmbeddedExceptionStack(
                        exception,
                        BasicException.Code.DEFAULT_DOMAIN,
                        BasicException.Code.NO_RESOURCE,
                        getInfo(
                            "class",
                            name, classLoader
                        )
                    )
                )
            );
    	}
    }

    /**
     * Load a system class
     *
     * @param     name
     *            fully qualified name of the desired class
     *
     * @return    the Class object for the class with the specified name. 
     *
     * @exception LinkageError
     *            if the linkage fails 
     * @exception ExceptionInInitializerError
     *            if the initialization provoked by this method fails 
     * @exception ClassNotFoundException
     *            if the class cannot be located by the kernel class loader
     */
    public static  Class getSystemClass(
        String name
    ) throws ClassNotFoundException {
        return getClass(
            name,
            ClassLoader.getSystemClassLoader()
        );
    }
    
    /**
     * Load a kernel class
     *
     * @param     name
     *            fully qualified name of the desired class
     *
     * @return    the Class object for the class with the specified name. 
     *
     * @exception LinkageError
     *            if the linkage fails 
     * @exception ExceptionInInitializerError
     *            if the initialization provoked by this method fails 
     * @exception ClassNotFoundException
     *            if the class cannot be located by the kernel class loader
     */
    public static  Class getKernelClass(
        String name
    ) throws ClassNotFoundException {
        return getClass(
            name,
            Classes.class.getClassLoader()
        );
    }
    
    /**
     * Load an application class
     *
     * @param     name
     *            fully qualified name of the desired class
     *
     * @return    the Class object for the class with the specified name. 
     *
     * @exception LinkageError
     *            if the linkage fails 
     * @exception ExceptionInInitializerError
     *            if the initialization provoked by this method fails 
     * @exception ClassNotFoundException
     *            if the class cannot be located by the kernel class loader
     */
    public static  Class  getApplicationClass(
        String name
    ) throws ClassNotFoundException {
        return getClass(
            name,
            getStandardClassLoader()
        );
    }
    
    /**
     * Finds all the resources with the given name. A resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * 

The name of a resource is a /-separated path name that * identifies the resource. * *

The search order is described in the documentation for {@link * #getResource(String)}.

* * @param name * The resource name * * @return An enumeration of {@link java.net.URL URL} objects for * the resource. If no resources could be found, the enumeration * will be empty. Resources that the class loader doesn't have * access to will not be in the enumeration. * * @throws IOException * If I/O errors occur */ public static Enumeration getResources(String name) throws IOException { return getStandardClassLoader().getResources(name); } //------------------------------------------------------------------------ // Proxy Factory //------------------------------------------------------------------------ /** * Create a new proxy instance * * @param invocationHandler * @param interfaces * * @return a newly created proxy instance * * @throws IllegalArgumentException if any of the restrictions on the * parameters that may be passed to getProxyClass * are violated * @throws NullPointerException if the interfaces array * argument or any of its elements are null, or * if the invocation handler, invocationHandler, is * null */ @SuppressWarnings("unchecked") public static T newProxyInstance( InvocationHandler invocationHandler, Class... interfaces ){ return (T) Proxy.newProxyInstance( getStandardClassLoader(), interfaces, invocationHandler ); } /** * Retrieve the interfaces implemented by an object class * * @param objectClass * * @return the interfaces implemented by the given object class */ public static Set> getInterfaces( Class objectClass ){ Set> interfaces = new LinkedHashSet>(); for( Class currentClass = objectClass; currentClass != null; currentClass = currentClass.getSuperclass() ){ for(Class currentInterface : currentClass.getInterfaces()) { interfaces.add(currentInterface); } } return interfaces; } /** * Get methods ordered by their declaration. As of Javadoc Class.getMethods() returns * the methods in any particular order. This method returns declared, non-abstract * methods first. * * @param clazz * @return */ public static List getOrderedMethods( Class clazz ) { // Assert that declared methods are processed first. As of Javadoc getMethods() returns // the methods in any particular order. List methods = new ArrayList(Arrays.asList(clazz.getDeclaredMethods())); // As of Javadoc: if clazz represents a type that has multiple declared methods with // the same name and parameter types, but different return types, then the returned array // has a Method object for each such method. Remove these inherited methods for(Iterator i = methods.iterator(); i.hasNext(); ) { Method method = i.next(); if(!Modifier.isAbstract(method.getModifiers())) { i.remove(); } } for(Method method: clazz.getMethods()) { if(!methods.contains(method)) { methods.add(method); } } return methods; } //------------------------------------------------------------------------ // Instance Factory //------------------------------------------------------------------------ /** * Create a new application class instance * * @param interfaceClass * @param className * @param arguments * * @return a new Instance * * @throws ClassNotFoundException * @throws IllegalArgumentException * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException */ @SuppressWarnings("unchecked") public static T newApplicationInstance( Class interfaceClass, String className, Object... arguments ) throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Class instanceClass = getApplicationClass(className); if(interfaceClass.isAssignableFrom(instanceClass)) { int argumentCount = arguments == null ? 0 : arguments.length; Constructors: for(Constructor constructor: instanceClass.getConstructors()) { Class[] parameters = constructor.getParameterTypes(); int parameterCount = parameters == null ? 0 : parameters.length; if(argumentCount == parameterCount) { for( int i = 0; i < argumentCount; i++ ){ final Object argument = arguments[i]; if( argument != null && !toObjectClass(parameters[i]).isInstance(argument) ) continue Constructors; } return (T) constructor.newInstance(arguments); } } throw new IllegalArgumentException( className + " has no constructor for the given arguments" ); } else { throw new ClassCastException ( className + " is not an instance of " + interfaceClass.getName() ); } } /** * Create a platform specific instance * * @param interfaceClass * @param arguments * * @return a new platform specific instance, or null in case of failure */ public static T newPlatformInstance( Class interfaceClass, Object... arguments ){ String interfaceName = interfaceClass.getName(); String className = Platform.getProperty(interfaceName).trim(); if(className == null || className.isEmpty()) { throw BasicException.initHolder( new RuntimeException( "Missing platform configuration entry " + interfaceName, BasicException.newEmbeddedExceptionStack( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.NO_RESOURCE, new BasicException.Parameter("interfaceName", interfaceName), new BasicException.Parameter("className", className), new BasicException.Parameter("arguments", arguments) ) ) ); } try { return newApplicationInstance( interfaceClass, className, arguments ); } catch (Exception exception) { throw BasicException.initHolder( new RuntimeException( "Unable to acquire an " + interfaceName + " instance", BasicException.newEmbeddedExceptionStack( exception, BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.NO_RESOURCE, new BasicException.Parameter("interfaceName", interfaceName), new BasicException.Parameter("className", className), new BasicException.Parameter("arguments", arguments) ) ) ); } } /** * Create a platform specific instance * * @param defaultClassName * @param interfaceClass * @param arguments * * @return a new platform specific instance * * @return a new platform specific instance * * @exception RuntimeException in case of failure */ public static T newPlatformInstance( String defaultClassName, Class interfaceClass, Object... arguments ){ String interfaceName = interfaceClass.getName(); String className = Platform.getProperty(interfaceName, defaultClassName).trim(); try { return newApplicationInstance( interfaceClass, className, arguments ); } catch (Exception exception) { throw BasicException.initHolder( new RuntimeException( "Unable to acquire an " + interfaceName + " instance", BasicException.newEmbeddedExceptionStack( exception, BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.NO_RESOURCE, new BasicException.Parameter("defaultClassName", defaultClassName), new BasicException.Parameter("interfaceName", interfaceName), new BasicException.Parameter("className", className), new BasicException.Parameter("arguments", arguments) ) ) ); } } /** * Return a clone of the object * * @param object the object to be cloned * * @return a clone, or null if the object is null * * @exception RuntimeException if cloning fails */ @SuppressWarnings("unchecked") public static T clone( T object ) { if(object == null) { return null; } final Class objectClass = object.getClass(); if(object instanceof Cloneable) { try { return (T) objectClass.getMethod( "clone" ).invoke( object ); } catch (InvocationTargetException exception) { final Throwable cause = exception.getCause(); throw BasicException.initHolder( new RuntimeException( "Unable to clone object", BasicException.newEmbeddedExceptionStack( cause, BasicException.Code.DEFAULT_DOMAIN, cause instanceof CloneNotSupportedException ? BasicException.Code.NOT_SUPPORTED : BasicException.Code.GENERIC, new BasicException.Parameter("objectClass", objectClass) ) ) ); } catch (IllegalAccessException exception) { throw BasicException.initHolder( new RuntimeException( "Unable to clone object", BasicException.newEmbeddedExceptionStack( exception, BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.SECURITY_FAILURE, new BasicException.Parameter("objectClass", objectClass) ) ) ); } catch (NoSuchMethodException exception) { throw BasicException.initHolder( new RuntimeException( "Unable to clone object", BasicException.newEmbeddedExceptionStack( exception, BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.NOT_IMPLEMENTED, new BasicException.Parameter("objectClass", objectClass) ) ) ); } } else { throw BasicException.initHolder( new RuntimeException( "Object is not Cloneable", BasicException.newEmbeddedExceptionStack( BasicException.Code.DEFAULT_DOMAIN, BasicException.Code.BAD_PARAMETER, new BasicException.Parameter("required", Cloneable.class.getName()), new BasicException.Parameter("actual", objectClass) ) ) ); } } //------------------------------------------------------------------------ // Class Application Class Loader Type //------------------------------------------------------------------------ enum ClassLoaderType { /** * Appliaction classes are loaded by the context class loader */ CONTEXT_CLASS_LOADER, /** * Application classes are loaded by the openMDX class loader */ KERNEL_CLASS_LOADER; private static final ClassLoaderType STANDARD = valueOf( Platform.getProperty("org.openmdx.kernel.loading.StandardClassLoaderType", CONTEXT_CLASS_LOADER.name()) ); /** * Determine the standard application and resource class loader type * * @return Returns the standard application and resource class loader type */ static ClassLoaderType getStandardClassLoaderType() { return STANDARD; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy