Package ceylon.modules.jboss.runtime

Source Code of ceylon.modules.jboss.runtime.CeylonModuleLoader

/*
* Copyright 2011 Red Hat inc. and third party contributors as noted
* by the author tags.
*
* 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 ceylon.modules.jboss.runtime;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.jboss.modules.DependencySpec;
import org.jboss.modules.LocalLoader;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleClassLoader;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.ModuleSpec;
import org.jboss.modules.ModuleSpec.Builder;
import org.jboss.modules.NativeLibraryResourceLoader;
import org.jboss.modules.ResourceLoader;
import org.jboss.modules.ResourceLoaderSpec;
import org.jboss.modules.filter.PathFilter;
import org.jboss.modules.filter.PathFilters;

import ceylon.modules.api.runtime.LogChecker;
import ceylon.modules.api.util.ModuleVersion;
import ceylon.modules.jboss.repository.ResourceLoaderProvider;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.cmr.api.ImportType;
import com.redhat.ceylon.cmr.api.JDKUtils;
import com.redhat.ceylon.cmr.api.ModuleDependencyInfo;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.common.Constants;
import com.redhat.ceylon.common.Versions;

/**
* Ceylon JBoss Module loader.
* It understands Ceylon repository notion.
*
* @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
*/
public class CeylonModuleLoader extends ModuleLoader {
    private static final ModuleIdentifier LANGUAGE;
    private static final ModuleIdentifier COMMON;
    private static final ModuleIdentifier CMR;
    private static final ModuleIdentifier TYPECHECKER;
    private static final ModuleIdentifier COMPILER;
    private static final ModuleIdentifier MAVEN;
    private static final ModuleIdentifier MODULES;
    private static final ModuleIdentifier JANDEX;
    private static final ModuleIdentifier LOGMANAGER;
    private static final ModuleIdentifier RUNTIME;
    private static final ModuleIdentifier ANTLR_ANTLR;
    private static final ModuleIdentifier ANTLR_STRINGTEMPLATE;
    private static final ModuleIdentifier ANTLR_RUNTIME;

    private static final String CEYLON_RUNTIME_PATH;
    private static final Set<ModuleIdentifier> BOOTSTRAP;

    private static final DependencySpec JDK_DEPENDENCY;
    private static final Set<String> JDK_MODULE_NAMES;

    private static final List<LogChecker> checkers;

    static {
        final String defaultVersion = System.getProperty(Constants.PROP_CEYLON_SYSTEM_VERSION, Versions.CEYLON_VERSION_NUMBER);
        LANGUAGE = ModuleIdentifier.create("ceylon.language", defaultVersion);
        COMMON = ModuleIdentifier.create("com.redhat.ceylon.common", defaultVersion);
        CMR = ModuleIdentifier.create("com.redhat.ceylon.module-resolver", defaultVersion);
        TYPECHECKER = ModuleIdentifier.create("com.redhat.ceylon.typechecker", defaultVersion);
        COMPILER = ModuleIdentifier.create("com.redhat.ceylon.compiler.java", defaultVersion);
        MAVEN = ModuleIdentifier.create("com.redhat.ceylon.maven-support", "2.0");
        MODULES = ModuleIdentifier.create("org.jboss.modules", "1.3.3.Final");
        JANDEX = ModuleIdentifier.create("org.jboss.jandex", "1.0.3.Final");
        LOGMANAGER = ModuleIdentifier.create("org.jboss.logmanager", "1.4.0.Final");
        RUNTIME = ModuleIdentifier.create("ceylon.runtime", defaultVersion);
        ANTLR_ANTLR = ModuleIdentifier.create("org.antlr.antlr", "2.7.7");
        ANTLR_STRINGTEMPLATE = ModuleIdentifier.create("org.antlr.stringtemplate", "3.2.1");
        ANTLR_RUNTIME = ModuleIdentifier.create("org.antlr.runtime", "3.4");

        CEYLON_RUNTIME_PATH = ModuleVersion.class.getPackage().getName().replace(".", "/");

        BOOTSTRAP = new HashSet<>();
        BOOTSTRAP.add(LANGUAGE);
        BOOTSTRAP.add(COMMON);
        BOOTSTRAP.add(CMR);
        BOOTSTRAP.add(TYPECHECKER);
        BOOTSTRAP.add(COMPILER);
        BOOTSTRAP.add(MAVEN);
        BOOTSTRAP.add(MODULES);
        BOOTSTRAP.add(JANDEX);
        BOOTSTRAP.add(LOGMANAGER);
        BOOTSTRAP.add(RUNTIME);

        Set<String> jdkPaths = new HashSet<>();
        JDK_MODULE_NAMES = new HashSet<>();
        // JDK
        for (String module : JDKUtils.getJDKModuleNames()) {
            Set<String> paths = JDKUtils.getJDKPathsByModule(module);
            jdkPaths.addAll(paths);
            JDK_MODULE_NAMES.add(module);
        }
        // Oracle
        for (String module : JDKUtils.getOracleJDKModuleNames()) {
            Set<String> paths = JDKUtils.getOracleJDKPathsByModule(module);
            JDK_MODULE_NAMES.add(module);
            jdkPaths.addAll(paths);
        }
        // always exported implicitely
        JDK_DEPENDENCY = DependencySpec.createSystemDependencySpec(jdkPaths, true);

        // add log checkers
        checkers = new ArrayList<>();
        for (LogChecker checker : ServiceLoader.load(LogChecker.class)) {
            checkers.add(checker);
        }
    }

    private RepositoryManager repository;
    private Map<ModuleIdentifier, List<DependencySpec>> dependencies = new ConcurrentHashMap<>();
    private Graph<ModuleIdentifier, ModuleIdentifier, Boolean> graph = new Graph<>();

    public CeylonModuleLoader(RepositoryManager repository) throws Exception {
        if (repository == null)
            throw new IllegalArgumentException("Null repository adapter");
        this.repository = repository;
        // initialise runtime modules
        init();
    }

    protected void init() throws Exception {
        // The runtime model needs knowledge of these modules existing at runtime, since the language module
        // implementation contains types from these modules
        ModuleLoader bootModuleLoader = org.jboss.modules.Module.getBootModuleLoader();
        for (ModuleIdentifier initialModule : Arrays.asList(LANGUAGE, COMMON, TYPECHECKER, COMPILER, CMR,
            ANTLR_ANTLR, ANTLR_RUNTIME, ANTLR_STRINGTEMPLATE)) {
            org.jboss.modules.Module module = bootModuleLoader.loadModule(initialModule);
            ArtifactResult moduleArtifactResult = findArtifact(initialModule);
            UtilRegistryTransformer.registerModule(initialModule.getName(), initialModule.getSlot(), moduleArtifactResult, SecurityActions.getClassLoader(module), false);
        }
    }

    /**
     * Update module.
     * Should be thread safe per module.
     *
     * @param module         the module to update
     * @param dependencySpec new dependency
     * @throws ModuleLoadException for any error
     */
    public void updateModule(org.jboss.modules.Module module, DependencySpec dependencySpec) throws ModuleLoadException {
        ModuleIdentifier mi = module.getIdentifier();
        List<DependencySpec> deps = dependencies.get(mi);
        if (deps == null) // should not really happen
            return;

        deps.add(dependencySpec);

        setAndRelinkDependencies(module, deps);
        refreshResourceLoaders(module);

        relink(mi, new HashSet<ModuleIdentifier>());
    }

    /**
     * Relink modules.
     *
     * @param mi      the current module identifier
     * @param visited already visited modules
     * @throws ModuleLoadException for any modules error
     */
    @SuppressWarnings({"unchecked"})
    private void relink(ModuleIdentifier mi, Set<ModuleIdentifier> visited) throws ModuleLoadException {
        if (visited.add(mi) == false)
            return;

        Graph.Vertex v = graph.getVertex(mi);
        if (v == null)
            return;

        org.jboss.modules.Module module = preloadModule(mi);
        relink(module);

        Set<Graph.Edge<ModuleIdentifier, Boolean>> in = v.getIn();
        for (Graph.Edge<ModuleIdentifier, Boolean> edge : in) {
            if (edge.getCost()) {
                Graph.Vertex<ModuleIdentifier, Boolean> from = edge.getFrom();
                relink(from.getValue(), visited);
            }
        }
    }

    @Override
    protected org.jboss.modules.Module preloadModule(ModuleIdentifier mi) throws ModuleLoadException {
        if (BOOTSTRAP.contains(mi))
            return org.jboss.modules.Module.getBootModuleLoader().loadModule(mi);

        return super.preloadModule(mi);
    }

    /**
     * Unload module.
     *
     * @param module the module
     */
    void unloadModule(org.jboss.modules.Module module) {
        dependencies.remove(module.getIdentifier());
        unloadModuleLocal(module);
    }

    protected ArtifactResult findArtifact(ModuleIdentifier mi) {
        final ArtifactContext context = new ArtifactContext(mi.getName(), mi.getSlot(), ArtifactContext.CAR, ArtifactContext.JAR);
        return repository.getArtifactResult(context);
    }

    protected boolean isLogging(List<DependencySpec> deps, Builder builder, ArtifactResult result) {
        for (LogChecker checker : checkers) {
            final List<ModuleDependencyInfo> replacements = checker.handle(result);
            if (replacements != null) {
                if (replacements.isEmpty()) {
                    throw new IllegalArgumentException(String.format("Log replacements cannot be empty - %s [%s]!", result, checker));
                }
                addLoggingModules(builder, deps, replacements);
                return true;
            }
        }
        return false;
    }

    @Override
    protected ModuleSpec findModule(ModuleIdentifier moduleIdentifier) throws ModuleLoadException {
        try {
            final ArtifactResult artifact = findArtifact(moduleIdentifier);
            if (artifact == null)
                return null;

            final File moduleFile = artifact.artifact();
            final boolean isDefault = RepositoryManager.DEFAULT_MODULE.equals(moduleIdentifier.getName());

            final List<DependencySpec> deps = new ArrayList<>();

            ModuleSpec.Builder builder = ModuleSpec.build(moduleIdentifier);
            // add module's jar
            ResourceLoader resourceLoader = ResourceLoaderProvider.getResourceLoader(moduleIdentifier, repository, moduleFile);
            // filter
            PathFilter filter = (artifact.filter() != null ? new CMRPathFilter(artifact.filter()) : PathFilters.acceptAll());
            // module resource root
            ResourceLoaderSpec rls = ResourceLoaderSpec.createResourceLoaderSpec(resourceLoader, filter);
            builder.addResourceRoot(rls);
            // add potential native lib lookup
            ResourceLoader nativeLoader = new NativeLibraryResourceLoader(new File(moduleFile.getParent(), "lib"));
            builder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(nativeLoader));

            Graph.Vertex<ModuleIdentifier, Boolean> vertex = graph.createVertex(moduleIdentifier, moduleIdentifier);

            DependencySpec lds = DependencySpec.createLocalDependencySpec();
            builder.addDependency(lds); // local resources
            deps.add(lds);

            if (isDefault == false) {
                Node<ArtifactResult> root = new Node<>();
                for (ArtifactResult i : artifact.dependencies()) {
                    final String name = i.name();

                    // route logging to JBoss LogManager
                    if (isLogging(deps, builder, i)) {
                        continue;
                    }

                    // skip JDK modules
                    if (JDK_MODULE_NAMES.contains(name)) {
                        continue;
                    }

                    if (i.importType() == ImportType.OPTIONAL) {
                        Node<ArtifactResult> current = root;
                        String[] tokens = name.split("\\.");
                        for (String token : tokens) {
                            Node<ArtifactResult> child = current.getChild(token);
                            if (child == null)
                                child = current.addChild(token);
                            current = child;
                        }
                        current.setValue(i);
                    } else {
                        DependencySpec mds = createModuleDependency(i);
                        builder.addDependency(mds);
                        deps.add(mds);
                    }

                    ModuleIdentifier mi = createModuleIdentifier(i);
                    Graph.Vertex<ModuleIdentifier, Boolean> dv = graph.createVertex(mi, mi);
                    Graph.Edge.create(i.importType() == ImportType.EXPORT, vertex, dv);
                }
                if (root.isEmpty() == false) {
                    LocalLoader onDemandLoader = new OnDemandLocalLoader(moduleIdentifier, this, root);
                    builder.setFallbackLoader(onDemandLoader);
                }
            }

            // automagically import the JDK module
            builder.addDependency(JDK_DEPENDENCY);
            // no need to track system deps -- cannot be updated anyway

            createModuleDependency(vertex, deps, builder, LANGUAGE, false);

            // add runtime utils
            final DependencySpec sds = DependencySpec.createModuleDependencySpec(
                PathFilters.match(CEYLON_RUNTIME_PATH),
                PathFilters.rejectAll(),
                this,
                RUNTIME,
                true
            );
            builder.addDependency(sds);
            deps.add(sds);
            Graph.Vertex<ModuleIdentifier, Boolean> sdsv = graph.createVertex(RUNTIME, RUNTIME);
            Graph.Edge.create(false, vertex, sdsv);

            dependencies.put(moduleIdentifier, deps);

            UtilRegistryTransformer transformer = new UtilRegistryTransformer(moduleIdentifier, artifact);
            builder.setClassFileTransformer(transformer);

            // make sure we set our own class loader factory so we can find the transformer back from the class loader
            // this is used in the language module to force module metamodel registration
            builder.setModuleClassLoaderFactory(new CeylonModuleClassLoader.CeylonModuleClassLoaderFactory(transformer));

            return builder.create();
        } catch (Exception e) {
            throw new ModuleLoadException(e);
        }
    }

    private void addLoggingModules(Builder builder, List<DependencySpec> deps, List<ModuleDependencyInfo> replacements) {
        for (ModuleDependencyInfo mi : replacements) {
            ModuleIdentifier identifier = ModuleIdentifier.create(mi.getName(), mi.getVersion());
            final DependencySpec dependency = DependencySpec.createModuleDependencySpec(
                PathFilters.acceptAll(),
                mi.isExport() ? PathFilters.acceptAll() : PathFilters.rejectAll(),
                this,
                identifier,
                mi.isOptional()
            );
            builder.addDependency(dependency);
            deps.add(dependency);
        }
    }

    protected void createModuleDependency(Graph.Vertex<ModuleIdentifier, Boolean> vertex, List<DependencySpec> deps, ModuleSpec.Builder builder, ModuleIdentifier mi, boolean optional) {
        final DependencySpec dependency = DependencySpec.createModuleDependencySpec(
            PathFilters.acceptAll(),
            PathFilters.rejectAll(),
            this,
            mi,
            optional
        );
        builder.addDependency(dependency);
        deps.add(dependency);

        Graph.Vertex<ModuleIdentifier, Boolean> lv = graph.createVertex(mi, mi);
        Graph.Edge.create(false, vertex, lv);
    }

    /**
     * Create module dependency from import.
     *
     * @param i the import
     * @return new module dependency
     */
    DependencySpec createModuleDependency(ArtifactResult i) {
        // this should never happen
        if (JDK_MODULE_NAMES.contains(i.name()))
            return JDK_DEPENDENCY;

        final ModuleIdentifier mi = createModuleIdentifier(i);
        final boolean export = (i.importType() == ImportType.EXPORT);
        return DependencySpec.createModuleDependencySpec(
            PathFilters.getMetaInfSubdirectoriesWithoutMetaInfFilter(), // import everything?
            (export ? PathFilters.acceptAll() : PathFilters.rejectAll()),
            this,
            mi,
            i.importType() == ImportType.OPTIONAL
        );
    }

    /**
     * Create module identifier.
     *
     * @param i the import
     * @return module identifer
     */
    static ModuleIdentifier createModuleIdentifier(ArtifactResult i) {
        return ModuleIdentifier.create(i.name(), i.version());
    }

    @Override
    public String toString() {
        return "Ceylon ModuleLoader: " + repository;
    }

    public void loadModuleSynchronous(String name, String version) throws ModuleLoadException{
        ModuleIdentifier moduleIdentifier = ModuleIdentifier.create(name, version);
        Module module = loadModule(moduleIdentifier);
        ModuleClassLoader classLoader = module.getClassLoader();
        if(classLoader instanceof CeylonModuleClassLoader){
            ((CeylonModuleClassLoader) classLoader).registerInMetaModel();
            ((CeylonModuleClassLoader) classLoader).waitForRegisterThreads();
        }
    }
}
TOP

Related Classes of ceylon.modules.jboss.runtime.CeylonModuleLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.