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

com.sun.enterprise.module.impl.ModuleImpl Maven / Gradle / Ivy

There is a newer version: 1.6.9
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2007-2011 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.module.impl;


import com.sun.enterprise.module.ImportPolicy;
import com.sun.enterprise.module.LifecyclePolicy;
import com.sun.enterprise.module.Module;
import com.sun.enterprise.module.ModuleChangeListener;
import com.sun.enterprise.module.ModuleDefinition;
import com.sun.enterprise.module.ModuleDependency;
import com.sun.enterprise.module.ModuleLifecycleListener;
import com.sun.enterprise.module.ModuleMetadata;
import com.sun.enterprise.module.InhabitantsDescriptor;
import com.sun.enterprise.module.ModuleState;
import com.sun.enterprise.module.ResolveError;
import com.sun.enterprise.module.common_impl.LogHelper;
import com.sun.hk2.component.Holder;
import com.sun.hk2.component.InhabitantsParser;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A module represents a set of resources accessible to third party modules. 
 * Each module has a module definition which defines its name, its list of 
 * exported resources and its dependencies to other modules. 
 * A module instance stores references to the class loader instances giving
 * access to the module's implementation. 
 * A module instance belongs to a ModuleRegistry which can be used
 * to get the list of available modules and/or get particular module
 * implementation. 
 * Modules can only satisfy their dependencies within the ModuleRegistry
 *  instance they are registered in.
 *
 * @author Jerome Dochez
 */
public final class ModuleImpl implements Module {
    
    private final ModuleDefinition moduleDef;
    private WeakReference publicCL;
    private volatile ModuleClassLoader privateCL;

    /**
     * Lazily loaded provider {@link Class}es from {@link com.sun.enterprise.module.ModuleMetadata}.
     * The key is the service class name. We can't use {@link Class} because that would cause leaks.
     */
    private final Map> serviceClasses = new ConcurrentHashMap>();

    /**
     * {@link ModulesRegistryImpl} that owns this module.
     * Always non-null.
     */
    private final ModulesRegistryImpl registry;
    private ModuleState state;
    private final List dependencies = new ArrayList();
    private final ArrayList listeners = new ArrayList();
    private final HashMap lastModifieds = new HashMap();
    private boolean shared=true;
    private boolean sticky=false;
    private LifecyclePolicy lifecyclePolicy = null;
    
    /** Creates a new instance of Module */
    /* package */ ModuleImpl(ModulesRegistryImpl registry, ModuleDefinition info) {
        assert registry!=null && info!=null;
        this.registry = registry;
        moduleDef = info;
        for (URI lib : info.getLocations()) {
            File f = new File(lib);
            if (f.exists()) {
                lastModifieds.put(f.getAbsolutePath(), f.lastModified());
            }

        }
        state = ModuleState.NEW;
    }
    
    /**
     * Return the ClassLoader  instance associated with this module.
     * Only designated public interfaces will be loaded and returned by 
     * this classloader.
     *
     * @return the public ClassLoader
     */
    public ClassLoaderFacade getClassLoader() {
        ClassLoaderFacade r=null;
        if (publicCL!=null)
            r = publicCL.get();
        if (r!=null)
            return r;

        ClassLoaderFacade facade = AccessController.doPrivileged(new PrivilegedAction() {
            public ClassLoaderFacade run() {
                return new ClassLoaderFacade(getPrivateClassLoader());
            }
        });
        facade.setPublicPkgs(moduleDef.getPublicInterfaces());
        publicCL = new WeakReference(facade);
        return facade;
    }
    
    /**
     * Return the private class loader for this module. This class loader will
     * be loading all the classes which are not explicitely exported in the 
     * module definition
     * @return the private ClassLoader instance
     */
    /*package*/ ModuleClassLoader getPrivateClassLoader() {
        if (privateCL==null) {
            synchronized(this) {
                if(privateCL==null) {
                    URI[] locations = moduleDef.getLocations();
                    URL[] urlLocations = new URL[locations.length];
                    for (int i=0;i() {
                        public ModuleClassLoader run() {
                            return new ModuleClassLoader(
                                ModuleImpl.this,
                                urls,
                                registry.getParentClassLoader());
                        }
                    });
                }
            }
        }
        return privateCL;
    }
    
    /**
     * Returns the module definition for this module instance
     * @return the module definition
     */
    public ModuleDefinition getModuleDefinition() {
        return moduleDef;
    }
    
    /**
     * Returns the registry owning this module
     * @return the registry owning the module
     */
    public ModulesRegistryImpl getRegistry() {
        return registry;
    }
    
    /**
     * Detach this module from its registry. This does not free any of the 
     * loaded resources. Only proper release of all references to the public
     * class loader will ensure module being garbage collected. 
     * Detached modules are orphan and will be garbage collected if resources
     * are properly disposed. 
     */
    public void detach() {        
        registry.remove(this);
    }
    
    /**
     * Return a String representation 
     * @return a descriptive String about myself
     */
    public String toString() {
        return "Module: " + moduleDef.getName() + "::" + (privateCL==null?"none":privateCL.toString());
    }
    
    /**
     * Add a new module change listener
     * @param listener the listener
     */
    public void addListener(ModuleChangeListener listener) {
        listeners.add(listener);
    }
    
    /**
     * Unregister a module change listener
     * @param listener the listener to unregister
     */
    public void removeListener(ModuleChangeListener listener) {
        listeners.remove(listener);
    }
    
    /**
     * fires a ModuleChange event to all listeners
     */
    protected void fireChangeEvent() {
        ArrayList list = new ArrayList(listeners);
        for (ModuleChangeListener listener : list) {
            listener.changed(this);
        }
        // registry listens by default
        registry.changed(this);
    }
    
    /**
     * Trigger manual refresh mechanism, the module will check all its 
     * URLs and generate change events if any of them has changed. This 
     * will allow the owning registry to force a module upgrade at next 
     * module request.
     */
    public void refresh() {
        URI[] urls = moduleDef.getLocations();
        boolean notify = false;
        for (URI lib : urls) {
            File f = new File(lib);
            if (f.exists() && lastModifieds.containsKey(f.getAbsolutePath())) {
                if (lastModifieds.get(f.getAbsolutePath()) !=f.lastModified()) {
                    //Utils.getDefaultLogger().info("Changed : " + this);
                    notify = true;
                    break;
                }
            }
        }
        if(notify) {
            fireChangeEvent();
        }
    }
    
    /**
     * Gets the metadata of this module.
     */
    public ModuleMetadata getMetadata() {
        return moduleDef.getMetadata();
    }

    /**
     * Parses all the inhabitants descriptors of the given name in this module.
     */
    void parseInhabitants(String name,InhabitantsParser parser) throws IOException {
        Holder holder = new Holder() {
            public ClassLoader get() {
                return getPrivateClassLoader();
            }
        };

        for (InhabitantsDescriptor d : getMetadata().getHabitats(name))
            parser.parse(d.createScanner(),holder);
    }

    /**
     * Ensure that this module is {@link ModuleState#RESOLVED resolved}.
     *
     * 

* If the module is already resolved, this method does nothing. * Otherwise, iterate over all declared ModuleDependency instances and use the * associated ModuleRegistry to resolve it. After successful * completion of this method, the module state is * {@link ModuleState#RESOLVED}. * * @throws com.sun.enterprise.module.ResolveError if any of the declared dependency of this module * cannot be satisfied */ public synchronized void resolve() throws ResolveError { // already resolved ? if (state==ModuleState.ERROR) throw new ResolveError("Module " + getName() + " is in ERROR state"); if (state.compareTo(ModuleState.RESOLVED)>=0) return; if (state==ModuleState.PREPARING) { Utils.identifyCyclicDependency(this, Logger.getAnonymousLogger()); throw new ResolveError("Cyclic dependency with " + getName()); } state = ModuleState.PREPARING; if (moduleDef.getImportPolicyClassName()!=null) { try { Class importPolicyClass = (Class) getPrivateClassLoader().loadClass(moduleDef.getImportPolicyClassName()); ImportPolicy importPolicy = importPolicyClass.newInstance(); importPolicy.prepare(this); } catch(ClassNotFoundException e) { state = ModuleState.ERROR; throw new ResolveError(e); } catch(java.lang.InstantiationException e) { state = ModuleState.ERROR; throw new ResolveError(e); } catch(IllegalAccessException e) { state = ModuleState.ERROR; throw new ResolveError(e); } } for (ModuleDependency dependency : moduleDef.getDependencies()) { ModuleImpl depModule = (ModuleImpl)registry.makeModuleFor(dependency.getName(), dependency.getVersion()); if (depModule==null) { state = ModuleState.ERROR; throw new ResolveError(dependency + " referenced from " + moduleDef.getName() + " is not resolved"); } //if (Utils.isLoggable(Level.INFO)) { // Utils.getDefaultLogger().info("For module" + getName() + " adding new dependent " + module.getName()); //} dependencies.add(depModule); } // once we have proper import/export filtering for modules, we can // build a look-up table to improve performance // build up the complete list of transitive dependency modules, without any duplication, // in a breadth-first fashion. The reason we do this in breadth-first is to reduce // the search time based on the assumption that classes tend to be discovered in close dependencies. List transitiveDependencies = new ArrayList(); Set transitiveDependenciesSet = new HashSet(); LinkedList q = new LinkedList(); q.addAll(dependencies); while(!q.isEmpty()) { ModuleImpl m = q.removeFirst(); if(transitiveDependenciesSet.add(m)) { // first time visited transitiveDependencies.add(m); m.resolve(); q.addAll(m.dependencies); } } for (ModuleImpl m : transitiveDependencies) { getPrivateClassLoader().addDelegate(m.getClassLoader()); } //Logger.global.info("Module " + getName() + " resolved"); state = ModuleState.RESOLVED; for (ModuleLifecycleListener l : registry.getLifecycleListeners()) { l.moduleResolved(this); } } /** * Forces module startup. In most cases, the runtime will take care * of starting modules when they are first used. There could be cases where * code need to manually start a sub module. Invoking this method will * move the module to the {@link ModuleState#READY ModuleState.READY}, the * {@link LifecyclePolicy#start Lifecycle.start} method will be invoked. */ public void start() throws ResolveError { if (state==ModuleState.READY) return; // ensure RESOLVED state resolve(); for (Module subModules : dependencies) { subModules.start(); } // time to initialize the lifecycle instance if (moduleDef.getLifecyclePolicyClassName()!=null) { try { Class lifecyclePolicyClass = (Class) getPrivateClassLoader().loadClass(moduleDef.getLifecyclePolicyClassName()); lifecyclePolicy = lifecyclePolicyClass.newInstance(); } catch(ClassNotFoundException e) { state = ModuleState.ERROR; throw new ResolveError("ClassNotFound : " + e.getMessage(), e); } catch(java.lang.InstantiationException e) { state = ModuleState.ERROR; throw new ResolveError(e); } catch(IllegalAccessException e) { state = ModuleState.ERROR; throw new ResolveError(e); } } if (lifecyclePolicy!=null) { lifecyclePolicy.start(this); } state = ModuleState.READY; //Logger.global.info("Module " + getName() + " started"); // module started. notify listeners for (ModuleLifecycleListener listener : registry.getLifecycleListeners()) listener.moduleStarted(this); } /** * Forces module stop. In most cases, the runtime will take care of stopping * modules when the last module user released its interest. However, in * certain cases, it may be interesting to manually stop the module. * Stopping the module means that the module is removed from the registry, * the class loader references are released (note : the class loaders will * only be released if all instances of any class loaded by them are gc'ed). * If a LifecyclePolicy for this module is defined, the * {@link LifecyclePolicy#stop(Module) Lifecycle.stop(Module)} * method will be called and finally the module state will be * returned to {@link ModuleState#NEW ModuleState.NEW}. * * @return true if unloading was successful */ public boolean stop() { if (sticky) { return false; } if (lifecyclePolicy!=null) { lifecyclePolicy.stop(this); lifecyclePolicy=null; } detach(); // we do NOT stop our sub modules which are shared... for (ModuleImpl subModule : dependencies) { if (!subModule.isShared()) { subModule.stop(); } } // release all sub modules class loaders privateCL = null; publicCL = null; dependencies.clear(); state = ModuleState.NEW; // notify interested listeners that I have stopped. for (ModuleLifecycleListener listener : registry.getLifecycleListeners()) { listener.moduleStopped(this); } return true; } /** * Returns the list of imported modules * @return the list of imported modules */ public List getImports() { resolve(); return Collections.unmodifiableList(dependencies); } /** * Create and add a new module to this module's list of * imports. * @param dependency new module's definition */ public Module addImport(ModuleDependency dependency) { ModuleImpl newModule; if (dependency.isShared()) { newModule = (ModuleImpl)registry.makeModuleFor(dependency.getName(), dependency.getVersion()); } else { newModule = registry.newPrivateModuleFor(dependency.getName(), dependency.getVersion()); } addImport(newModule); return newModule; } /** * Returns the module's state * @return the module's state */ public ModuleState getState() { return state; } public void addImport(Module module) { //if (Utils.isLoggable(Level.INFO)) { // Utils.getDefaultLogger().info("For module" + getName() + " adding new dependent " + module.getName()); //} // TODO: this doesn't expose newly added module to // other modules that depend on this module. // but the notion of adding dependencies at runtime is broken anyway. if (!dependencies.contains(module)) { dependencies.add((ModuleImpl)module); getPrivateClassLoader().addDelegate(module.getClassLoader()); } } public void removeImport(ModuleImpl module) { // TODO: this doesn't hide removed module from // other modules that depend on this module. // but the notion of adding dependencies at runtime is broken anyway. if (dependencies.contains(module)) { dependencies.remove(module); getPrivateClassLoader().removeDelegate(module.getClassLoader()); } } /** * Short-cut for {@code getModuleDefinition().getName()}. */ public String getName() { if (getModuleDefinition()!=null) { return getModuleDefinition().getName(); } return "unknown module"; } /** * Returns true if this module is sharable. A sharable module means that * onlu one instance of the module classloader will be used by all users. * * @return true if this module is sharable. */ public boolean isShared() { return shared; } /** * Sets the sharable flag. Setting the flag to false means that the moodule * class loader should not be shared among module owners. * @param sharable set to true to share the module */ void setShared(boolean sharable) { this.shared = sharable; } /** * Returns true if the module is sticky. A sticky module cannot be stopped or * unloaded. Once a sticky module is loaded or started, it will stay in the * JVM until it exists. * @return true is the module is sticky */ public boolean isSticky() { return sticky; } /** * Sets the sticky flag. * @param sticky true if the module should stick around */ public void setSticky(boolean sticky) { this.sticky = sticky; } @SuppressWarnings({"unchecked"}) public Iterable> getProvidersClass(Class serviceClass) { return (Iterable)getProvidersClass(serviceClass.getName()); } public Iterable getProvidersClass(String name) { List r = serviceClasses.get(name); if(r!=null) return r; // the worst case scenario in the race situation is we end up creating the same list twice, // which is not a big deal. for( String provider : getMetadata().getEntry(name).providerNames) { if(r==null) r = new ArrayList(); try { r.add(getPrivateClassLoader().loadClass(provider)); } catch (ClassNotFoundException e) { LogHelper.getDefaultLogger().log(Level.SEVERE, "Failed to load "+provider+" from "+getName(),e); } } if(r==null) r = Collections.emptyList(); serviceClasses.put(name, r); return r; } /** * Returns true if this module has any provider for the given service class. */ public boolean hasProvider(Class serviceClass) { String name = serviceClass.getName(); List v = serviceClasses.get(name); if(v!=null && !v.isEmpty()) return true; return getMetadata().getEntry(name).hasProvider(); } public void dumpState(PrintStream writer) { writer.println("Module " + getName() + " Dump"); writer.println("State " + getState()); for (Module imported : getImports()) { writer.println("Depends on " + imported.getName()); } if (publicCL!=null) { ClassLoaderFacade cloader = publicCL.get(); cloader.dumpState(writer); } } public void uninstall() { // Not Applicable in HK2 as there is no persistent module cache // implemented in HK2 yet. } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy