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

org.fabric3.fabric.builder.classloader.ClassLoaderBuilderImpl Maven / Gradle / Ivy

The newest version!
/*
 * Fabric3
 * Copyright (c) 2009-2013 Metaform Systems
 *
 * Fabric3 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version, with the
 * following exception:
 *
 * Linking this software statically or dynamically with other
 * modules is making a combined work based on this software.
 * Thus, the terms and conditions of the GNU General Public
 * License cover the whole combination.
 *
 * As a special exception, the copyright holders of this software
 * give you permission to link this software with independent
 * modules to produce an executable, regardless of the license
 * terms of these independent modules, and to copy and distribute
 * the resulting executable under terms of your choice, provided
 * that you also meet, for each linked independent module, the
 * terms and conditions of the license of that module. An
 * independent module is a module which is not derived from or
 * based on this software. If you modify this software, you may
 * extend this exception to your version of the software, but
 * you are not obligated to do so. If you do not wish to do so,
 * delete this exception statement from your version.
 *
 * Fabric3 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the
 * GNU General Public License along with Fabric3.
 * If not, see .
 *
 * ----------------------------------------------------
 *
 * Portions originally based on Apache Tuscany 2007
 * licensed under the Apache 2.0 license.
 *
 */
package org.fabric3.fabric.builder.classloader;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.oasisopen.sca.annotation.EagerInit;
import org.oasisopen.sca.annotation.Reference;

import org.fabric3.api.annotation.monitor.Monitor;
import org.fabric3.host.RuntimeMode;
import org.fabric3.host.runtime.HostInfo;
import org.fabric3.spi.builder.classloader.ClassLoaderListener;
import org.fabric3.spi.builder.classloader.ClassLoaderWireBuilder;
import org.fabric3.spi.classloader.ClassLoaderRegistry;
import org.fabric3.spi.classloader.MultiParentClassLoader;
import org.fabric3.spi.contribution.ContributionResolver;
import org.fabric3.spi.model.os.Library;
import org.fabric3.spi.contribution.MetaDataStore;
import org.fabric3.spi.contribution.ResolutionException;
import org.fabric3.spi.contribution.archive.ClasspathProcessorRegistry;
import org.fabric3.spi.model.physical.PhysicalClassLoaderDefinition;
import org.fabric3.spi.model.physical.PhysicalClassLoaderWireDefinition;

import static org.fabric3.host.Names.HOST_CONTRIBUTION;

/**
 * Default implementation of ClassLoaderBuilder.
 */
@EagerInit
public class ClassLoaderBuilderImpl implements ClassLoaderBuilder {
    private ClassLoaderWireBuilder wireBuilder;
    private ClassLoaderRegistry classLoaderRegistry;
    private ClasspathProcessorRegistry classpathProcessorRegistry;
    private ContributionResolver resolver;
    private ClassLoaderTracker tracker;
    private List listeners;
    private HostInfo info;
    private MetaDataStore metaDataStore;
    private Field sysPathsField;

    public ClassLoaderBuilderImpl(@Reference ClassLoaderWireBuilder wireBuilder,
                                  @Reference ClassLoaderRegistry classLoaderRegistry,
                                  @Reference ClasspathProcessorRegistry classpathProcessorRegistry,
                                  @Reference ContributionResolver resolver,
                                  @Reference ClassLoaderTracker tracker,
                                  @Reference MetaDataStore metaDataStore,
                                  @Reference HostInfo info,
                                  @Monitor ClassLoaderBuilderMonitor monitor) {
        this.wireBuilder = wireBuilder;
        this.classLoaderRegistry = classLoaderRegistry;
        this.classpathProcessorRegistry = classpathProcessorRegistry;
        this.resolver = resolver;
        this.tracker = tracker;
        this.metaDataStore = metaDataStore;
        this.info = info;
        this.listeners = Collections.emptyList();
        initializeSysPaths(info, monitor);

    }

    @Reference(required = false)
    public void setListeners(List listeners) {
        this.listeners = listeners;
    }

    public void build(PhysicalClassLoaderDefinition definition) throws ClassLoaderBuilderException {
        URI uri = definition.getUri();
        int count = tracker.increment(uri);
        ClassLoader classLoader = classLoaderRegistry.getClassLoader(uri);
        if (classLoader != null) {
            // The classloader was already loaded. The classloader will already be created if: it is the boot classloader; the environment is
            // single-VM as classloaders are shared between the contribution and runtime infrastructure; two composites are deployed individually
            // from the same contribution.
            for (PhysicalClassLoaderWireDefinition wireDefinition : definition.getWireDefinitions()) {
                URI target = wireDefinition.getTargetClassLoader();
                classLoader = classLoaderRegistry.getClassLoader(target);
                tracker.incrementImported(classLoader);
            }
            notifyListenersBuild(count, classLoader);
            return;
        }
        if (info.supportsClassLoaderIsolation()) {
            buildIsolatedClassLoaderEnvironment(definition);
        } else {
            buildCommonClassLoaderEnvironment(definition);
        }
        notifyListenersBuild(count, classLoader);
    }

    public void destroy(URI uri) throws ClassLoaderBuilderException {
        ClassLoader classLoader = classLoaderRegistry.getClassLoader(uri);
        int val = tracker.decrement(classLoader);
        if (val == 0 && metaDataStore.find(uri) == null) {
            // Note the MetaDataStore is used to determine if a contribution classloader must be tracked. If a contribution is registered in the
            // store, it is installed as an extension of the base runtime distribution and should only be uninstalled explicitly.
            try {
                classLoaderRegistry.unregister(uri);
                // release the previously resolved contribution
                resolver.release(uri);
            } catch (ResolutionException e) {
                throw new ClassLoaderBuilderException("Error releasing artifact: " + uri.toString(), e);
            }
            for (ClassLoaderListener listener : listeners) {
                listener.onUndeploy(classLoader);
            }
        } else if (val == 0) {
            // single VM, do not remove the classloader since it is used by the installed contribution. Just notify listeners
            for (ClassLoaderListener listener : listeners) {
                listener.onUndeploy(classLoader);
            }
        }
    }

    private void notifyListenersBuild(int count, ClassLoader classLoader) {
        if (count == 1) {
            for (ClassLoaderListener listener : listeners) {
                listener.onDeploy(classLoader);
            }
        }
    }

    private void buildCommonClassLoaderEnvironment(PhysicalClassLoaderDefinition definition) {
        URI uri = definition.getUri();
        // Create an alias to the host classloader which contains all contribution artifacts in a non-isolated environment.
        // This simulates multiple classloaders
        ClassLoader hostClassLoader = classLoaderRegistry.getClassLoader(HOST_CONTRIBUTION);
        classLoaderRegistry.register(uri, hostClassLoader);
    }

    private void buildIsolatedClassLoaderEnvironment(PhysicalClassLoaderDefinition definition) throws ClassLoaderBuilderException {
        URI uri = definition.getUri();
        // build the classloader using the locally cached resources
        ClassLoader hostClassLoader = classLoaderRegistry.getClassLoader(HOST_CONTRIBUTION);
        MultiParentClassLoader loader;
        if (definition.isProvisionArtifact()) {
            URL[] classpath = resolveClasspath(definition);
            loader = new MultiParentClassLoader(uri, classpath, hostClassLoader);
            setSysPathsField(loader);
        } else {
            loader = new MultiParentClassLoader(uri, hostClassLoader);
        }
        for (PhysicalClassLoaderWireDefinition wireDefinition : definition.getWireDefinitions()) {
            wireBuilder.build(loader, wireDefinition);
            URI target = wireDefinition.getTargetClassLoader();
            ClassLoader classLoader = classLoaderRegistry.getClassLoader(target);
            tracker.incrementImported(classLoader);
        }
        classLoaderRegistry.register(uri, loader);
    }

    /**
     * Resolves classpath urls.
     *
     * @param definition the physical classpath definition
     * @return the resolved classpath urls
     * @throws ClassLoaderBuilderException if an error occurs resolving a url
     */
    private URL[] resolveClasspath(PhysicalClassLoaderDefinition definition) throws ClassLoaderBuilderException {
        URI uri = definition.getUri();
        try {
            // resolve the remote contributions and cache them locally
            URL resolvedUrl = resolver.resolve(uri);
            // introspect and expand if necessary
            List classpath = new ArrayList();
            List libraries = definition.getLibraries();
            List archiveClasspath = classpathProcessorRegistry.process(resolvedUrl, libraries);
            classpath.addAll(archiveClasspath);
            return classpath.toArray(new URL[classpath.size()]);
        } catch (ResolutionException e) {
            throw new ClassLoaderBuilderException("Error resolving artifact: " + uri.toString(), e);
        } catch (IOException e) {
            throw new ClassLoaderBuilderException("Error processing: " + uri.toString(), e);
        }
    }

    /**
     * Sets the native libraries path by setting the classloader sysPathsField to null. This will force the classloader to reinitialize the field to
     * JAVA_LIBRARY_PATH.
     *
     * @param loader the classloader
     */
    private void setSysPathsField(MultiParentClassLoader loader) {
        if (sysPathsField == null) {
            // not supported on this JVM
            return;
        }
        try {
            sysPathsField.set(loader, null);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }

    /**
     * Performs a workaround to enable specifying the path for native libraries on a per-extension classloader basis.
     *
     * @param info    the runtime info
     * @param monitor the monitor
     */
    private void initializeSysPaths(HostInfo info, ClassLoaderBuilderMonitor monitor) {
        try {
            sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
            sysPathsField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            // fail silently since sys_paths may not be supported on all JVMs, e.g. J9
            if (RuntimeMode.PARTICIPANT == info.getRuntimeMode()) {
                // only issue info if on participant since contribution loading will issue a warning as well. In VM mode where the two subsystems
                // are collocated, this would result in duplicate messages
                monitor.nativeLibrariesNotSupported();
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy