org.sourcepit.maven.bootstrap.core.AbstractBootstrapper.java Source code

Java tutorial

Introduction

Here is the source code for org.sourcepit.maven.bootstrap.core.AbstractBootstrapper.java

Source

/*
 * Copyright 2014 Bernd Vogt and others.
 * 
 * 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 org.sourcepit.maven.bootstrap.core;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.inject.Inject;

import org.apache.maven.ArtifactFilterManager;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.classrealm.ClassRealmManager;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.internal.DependencyContext;
import org.apache.maven.lifecycle.internal.MojoExecutor;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.project.ProjectSorter;
import org.apache.maven.repository.RepositorySystem;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.ClassWorldListener;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
import org.sourcepit.maven.bootstrap.internal.core.ExtensionDescriptor;
import org.sourcepit.maven.bootstrap.internal.core.ExtensionDescriptorReader;
import org.sourcepit.maven.bootstrap.internal.core.PluginConfigurationReader;
import org.sourcepit.maven.bootstrap.internal.core.ReactorReader;
import org.sourcepit.maven.bootstrap.participation.BootstrapParticipant;
import org.sourcepit.maven.bootstrap.participation.BootstrapParticipant2;
import org.sourcepit.maven.exec.intercept.MavenExecutionParticipant;

public abstract class AbstractBootstrapper implements MavenExecutionParticipant {
    @Inject
    private Logger logger;

    @Inject
    private DefaultPlexusContainer plexusContainer;

    @Inject
    private LegacySupport legacySupport;

    @Inject
    private ProjectBuilder projectBuilder;

    @Inject
    private RepositorySystem repositorySystem;

    @Inject
    private ResolutionErrorHandler resolutionErrorHandler;

    @Inject
    private ClassRealmManager classRealmManager;

    @Inject
    private MojoExecutor mojoExecutor;

    @Inject
    private ArtifactFilterManager artifactFilterManager;

    private final ImportEnforcer importEnforcer;

    private final Set<String> extensionRealmPrefixes = new HashSet<String>();

    private final Map<MavenSession, MavenSession> actualToBootSession = new HashMap<MavenSession, MavenSession>();
    private final Map<MavenSession, MavenSession> bootToActualSession = new HashMap<MavenSession, MavenSession>();
    private final Map<Object, Object> bootContext = new HashMap<Object, Object>();

    private final String extensionKey;

    public AbstractBootstrapper(String groupId, String artifactId) {
        this.extensionKey = groupId + ":" + artifactId;

        final List<String> imports = new ArrayList<String>();
        imports.add("javax.inject.*");
        imports.add("com.google.inject.*");
        imports.add("com.google.inject.name.*");
        imports.add("org.sonatype.inject.*");
        imports.add("org.slf4j.*");
        imports.add("org.slf4j.impl.*");

        imports.add(ImportEnforcer.toImportPattern(BootstrapParticipant.class));
        imports.add(ImportEnforcer.toImportPattern(BootstrapParticipant2.class));

        extensionRealmPrefixes.add("extension>" + extensionKey);

        importEnforcer = new ImportEnforcer(getClass().getClassLoader(), extensionRealmPrefixes, imports);
    }

    public void executionStarted(MavenSession actualSession, MavenExecutionRequest executionRequest)
            throws MavenExecutionException {
        final MavenSession bootSession = createBootSession(actualSession);

        final List<File> descriptors = getProjectDescriptors(bootSession);
        if (descriptors.isEmpty()) {
            logger.info("Skipping bootstrapper " + extensionKey + ". No projects found.");
            return;
        }

        mapSessions(actualSession, bootSession);

        logger.info("Executing bootstrapper " + extensionKey + "...");

        plexusContainer.getContainerRealm().getWorld().addListener(importEnforcer);

        final MavenSession oldSession = legacySupport.getSession();
        try {
            legacySupport.setSession(bootSession);
            setupBootSession(bootSession, descriptors);

            final List<MavenProject> projects = bootSession.getProjects();
            if (projects.size() > 1) {
                logger.info("");
                logger.info("------------------------------------------------------------------------");
                logger.info("Bootstrapper Build Order:");
                logger.info("");
                for (MavenProject project : projects) {
                    logger.info(project.getName());
                }
            }

            performBootSession(bootSession);
            adjustActualSession(bootSession, actualSession);

            logger.info("");
            logger.info("------------------------------------------------------------------------");
            logger.info("Finished bootstrapper " + extensionKey);
        } finally {
            legacySupport.setSession(oldSession);
        }
    }

    private MavenSession createBootSession(MavenSession actualSession) {
        final DefaultRepositorySystemSession repositorySession = new DefaultRepositorySystemSession(
                actualSession.getRepositorySession());
        final DefaultMavenExecutionRequest executionRequest = (DefaultMavenExecutionRequest) DefaultMavenExecutionRequest
                .copy(actualSession.getRequest());
        // fix: copy ignores start time...
        executionRequest.setStartTime(actualSession.getRequest().getStartTime());
        // fix: copy ignores project building request...
        executionRequest.setProjectBuildingConfiguration(
                new DefaultProjectBuildingRequest(actualSession.getRequest().getProjectBuildingRequest()));
        final DefaultMavenExecutionResult executionResult = new DefaultMavenExecutionResult();
        return new MavenSession(plexusContainer, repositorySession, executionRequest, executionResult);
    }

    private void mapSessions(MavenSession actualSession, final MavenSession bootSession) {
        actualToBootSession.put(actualSession, bootSession);
        bootToActualSession.put(bootSession, actualSession);
    }

    private void setupBootSession(MavenSession bootSession, Collection<File> descriptors) {
        bootSession.setProjects(buildBootstrapProjects(bootSession, descriptors));

        try {
            Map<String, MavenProject> projectMap = getProjectMap(bootSession.getProjects());
            DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) bootSession
                    .getRepositorySession();
            repoSession.setWorkspaceReader(ChainedWorkspaceReader.newInstance(new ReactorReader(projectMap),
                    repoSession.getWorkspaceReader()));
        } catch (org.apache.maven.DuplicateProjectException e) {
            throw new IllegalStateException(e);
        }
    }

    private void performBootSession(final MavenSession bootSession) {
        for (MavenProject bootProject : bootSession.getProjects()) {
            bootSession.setCurrentProject(bootProject);

            final List<ClassRealm> bootExtensionClassRealms = discoverBootExtensionClassRealms(bootProject);
            for (ClassRealm bootExtensionClassRealm : bootExtensionClassRealms) {
                performBootProject(bootSession, bootProject, bootExtensionClassRealm);
            }
        }
    }

    private void performBootProject(MavenSession bootSession, MavenProject bootProject,
            ClassRealm bootExtensionClassRealm) {
        ensureDependenciesAreResolved(bootSession);

        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(bootExtensionClassRealm);
        try {
            final List<?> bootParticipants = discoverBootstrapParticipants(bootSession, bootProject,
                    bootExtensionClassRealm);
            for (Object bootParticipant : bootParticipants) {
                if (bootParticipant instanceof BootstrapParticipant) {
                    ((BootstrapParticipant) bootParticipant).beforeBuild(bootSession, bootProject,
                            bootToActualSession.get(bootSession));
                } else {
                    ((BootstrapParticipant2) bootParticipant).beforeBuild(bootSession, bootProject, bootContext);
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }

    private void ensureDependenciesAreResolved(MavenSession session) {
        final MojoDescriptor mojoDescriptor = new MojoDescriptor();
        mojoDescriptor.setAggregator(false);
        // mojoDescriptor.setDependencyCollectionRequired(getDependencyResolutionRequired());
        mojoDescriptor.setDependencyResolutionRequired(getDependencyResolutionRequired());

        final MojoExecution mojoExecution = new MojoExecution(mojoDescriptor);

        final DependencyContext dependencyContext = mojoExecutor.newDependencyContext(session,
                Collections.singletonList(mojoExecution));

        try {
            mojoExecutor.ensureDependenciesAreResolved(mojoDescriptor, session, dependencyContext);
        } catch (LifecycleExecutionException e) {
            throw new IllegalStateException(e);
        }
    }

    private Map<String, MavenProject> getProjectMap(List<MavenProject> projects)
            throws org.apache.maven.DuplicateProjectException {
        Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>();
        Map<String, List<File>> collisions = new LinkedHashMap<String, List<File>>();

        for (MavenProject project : projects) {
            String projectId = ArtifactUtils.key(project.getGroupId(), project.getArtifactId(),
                    project.getVersion());

            MavenProject collision = index.get(projectId);

            if (collision == null) {
                index.put(projectId, project);
            } else {
                List<File> pomFiles = collisions.get(projectId);

                if (pomFiles == null) {
                    pomFiles = new ArrayList<File>(Arrays.asList(collision.getFile(), project.getFile()));
                    collisions.put(projectId, pomFiles);
                } else {
                    pomFiles.add(project.getFile());
                }
            }
        }

        if (!collisions.isEmpty()) {
            throw new org.apache.maven.DuplicateProjectException("Two or more projects in the reactor"
                    + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
                    + " is unique for each project: " + collisions, collisions);
        }

        return index;
    }

    protected abstract String getDependencyResolutionRequired();

    protected abstract void adjustActualSession(MavenSession bootSession, MavenSession actualSession);

    public void executionEnded(MavenSession actualSession, MavenExecutionResult executionResult) {
        final MavenSession bootSession = actualToBootSession.remove(actualSession);
        if (bootSession == null) {
            return;
        }

        final MavenSession oldSession = legacySupport.getSession();
        try {
            bootToActualSession.remove(bootSession);

            legacySupport.setSession(bootSession);
            shutdownBootSession(bootSession);
        } finally {
            legacySupport.setSession(oldSession);
        }

        plexusContainer.getContainerRealm().getWorld().removeListener(importEnforcer);
    }

    private void shutdownBootSession(MavenSession bootSession) {
        for (MavenProject bootProject : bootSession.getProjects()) {
            bootSession.setCurrentProject(bootProject);

            final List<ClassRealm> bootExtensionClassRealms = discoverBootExtensionClassRealms(bootProject);
            for (ClassRealm bootExtensionClassRealm : bootExtensionClassRealms) {
                shutdownBootProject(bootSession, bootProject, bootExtensionClassRealm);
            }
        }
    }

    private void shutdownBootProject(MavenSession bootSession, MavenProject bootProject,
            ClassRealm bootExtensionClassRealm) {
        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(bootExtensionClassRealm);
        try {
            final List<?> bootParticipants = discoverBootstrapParticipants(bootSession, bootProject,
                    bootExtensionClassRealm);
            for (Object bootParticipant : bootParticipants) {
                if (bootParticipant instanceof BootstrapParticipant) {
                    ((BootstrapParticipant) bootParticipant).afterBuild(bootSession, bootProject,
                            bootToActualSession.get(bootSession));
                } else {
                    ((BootstrapParticipant2) bootParticipant).afterBuild(bootSession, bootProject, bootContext);
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }

    private List<?> discoverBootstrapParticipants(MavenSession bootSession, MavenProject bootProject,
            ClassRealm bootExtensionClassRealm) {
        final String key = BootstrapParticipant.class.getName() + "@" + bootExtensionClassRealm.getId();

        List<?> bootstrapParticipants = (List<?>) bootProject.getContextValue(key);
        if (bootstrapParticipants == null) {
            bootstrapParticipants = lookupBootstrapParticipants(bootSession, bootProject, bootExtensionClassRealm);
            bootProject.setContextValue(key, bootstrapParticipants);
        }
        return bootstrapParticipants;
    }

    private List<?> lookupBootstrapParticipants(MavenSession bootSession, MavenProject bootProject,
            ClassRealm bootExtensionClassRealm) {
        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(bootExtensionClassRealm);

        final ClassRealm oldRealm = plexusContainer.getLookupRealm();
        try {
            plexusContainer.setLookupRealm(bootExtensionClassRealm);

            addCustomClassLoaders(bootSession, bootProject, bootExtensionClassRealm);

            final List<Object> result = new ArrayList<Object>();
            final List<BootstrapParticipant> p1;
            try {
                p1 = plexusContainer.lookupList(BootstrapParticipant.class);
            } catch (ComponentLookupException e) {
                throw new IllegalStateException(e);
            }
            result.addAll(p1);

            final List<BootstrapParticipant2> p2;
            try {
                p2 = plexusContainer.lookupList(BootstrapParticipant2.class);
            } catch (ComponentLookupException e) {
                throw new IllegalStateException(e);
            }
            result.addAll(p2);

            return result;
        } finally {
            plexusContainer.setLookupRealm(oldRealm);
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }

    protected void addCustomClassLoaders(MavenSession bootSession, MavenProject bootProject,
            ClassRealm extensionRealm) {
        addExtensionExtensionsClassLoaders(bootSession, bootProject, extensionRealm);
    }

    protected final void addExtensionExtensionsClassLoaders(MavenSession bootSession, MavenProject bootProject,
            ClassRealm extensionRealm) {
        if (isAllowExtensionExtensions(bootSession, bootProject)) {
            final List<Dependency> extensions = getExtensionExtensions(bootSession, bootProject);
            if (extensions != null) {
                for (Dependency extension : extensions) {
                    final ClassRealm extRealm = getExtensionExtensionRealm(bootSession, bootProject, extensionRealm,
                            extension);
                    plexusContainer.discoverComponents(extRealm);
                }
            }
        }
    }

    protected abstract boolean isAllowExtensionExtensions(MavenSession bootSession, MavenProject bootProject);

    private ClassRealm getExtensionExtensionRealm(MavenSession bootSession, MavenProject bootProject,
            ClassRealm extensionRealm, Dependency extension) {
        final String realmId = extensionRealm.getId() + "@" + extension.toString();

        // don't create unnecessary class loaders (to prevent issues with EMF package registry with relates on current ctx
        // class loader...)
        try {
            return extensionRealm.getWorld().getRealm(realmId);
        } catch (NoSuchRealmException e) {
        }

        final ClassRealm newRealm = newRealm(extensionRealm.getWorld(), realmId);

        final URL[] urls = resolveURLs(bootSession, bootProject, extension);
        for (int j = 0; j < urls.length; j++) {
            newRealm.addURL(urls[j]);
        }

        newRealm.importFrom(classRealmManager.getCoreRealm(), "org.sonatype.plexus.components");

        importEnforcer.addBootstrapImports(newRealm);

        if (extensionRealm.getURLs().length > 0) {
            final ExtensionDescriptor extensionDescriptor = ExtensionDescriptorReader
                    .read(extensionRealm.getURLs()[0]);
            if (extensionDescriptor == null) {
                newRealm.importFrom(extensionRealm, "");
            } else {
                for (String exportedPackage : extensionDescriptor.getExportedPackages()) {
                    newRealm.importFrom(extensionRealm, exportedPackage);
                }
                newRealm.importFrom(classRealmManager.getMavenApiRealm(), "");
                // org.apache.maven.bridge currently not exposed via Maven Api (3.2.3) but required by API components, e.g
                // org.apache.maven.project.DefaultProjectBuilder.
                newRealm.importFrom(classRealmManager.getCoreRealm(), "org.apache.maven.bridge");
            }
        }

        return newRealm;
    }

    private URL[] resolveURLs(MavenSession bootSession, MavenProject bootProject, Dependency extension) {
        final ArtifactResolutionResult result = resolve(bootSession, bootProject, extension);

        final Set<Artifact> artifacts = result.getArtifacts();

        final URL[] urls = new URL[artifacts.size()];
        int i = 0;
        for (Iterator<org.apache.maven.artifact.Artifact> it = artifacts.iterator(); it.hasNext(); i++) {
            final org.apache.maven.artifact.Artifact artifact = it.next();
            try {
                urls[i] = artifact.getFile().toURI().toURL();
            } catch (MalformedURLException e) {
                throw new IllegalStateException(e);
            }
        }
        return urls;
    }

    private ClassRealm newRealm(ClassWorld world, String id) {
        synchronized (world) {
            String realmId = id;

            Random random = new Random();

            while (true) {
                try {
                    ClassRealm classRealm = world.newRealm(realmId, null);

                    if (logger.isDebugEnabled()) {
                        logger.debug("Created new class realm " + realmId);
                    }

                    return classRealm;
                } catch (DuplicateRealmException e) {
                    realmId = id + '-' + random.nextInt();
                }
            }
        }
    }

    protected List<Dependency> getExtensionExtensions(MavenSession bootSession, MavenProject bootProject) {
        return PluginConfigurationReader.readExtensions(bootProject, extensionKey);
    }

    private ArtifactResolutionResult resolve(MavenSession session, MavenProject project, Dependency dependency) {
        final ArtifactResolutionRequest request = new ArtifactResolutionRequest();
        request.setResolveRoot(true);
        request.setResolveTransitively(true);

        final AndArtifactFilter artifactFilter = new AndArtifactFilter();
        artifactFilter.add(new ScopeArtifactFilter(org.apache.maven.artifact.Artifact.SCOPE_RUNTIME_PLUS_SYSTEM));
        artifactFilter.add(new ExclusionSetFilter(artifactFilterManager.getCoreArtifactExcludes()));

        request.setResolutionFilter(artifactFilter);
        request.setCollectionFilter(artifactFilter);

        final MavenExecutionRequest executionRequest = session.getRequest();
        request.setForceUpdate(executionRequest.isUpdateSnapshots());
        request.setServers(executionRequest.getServers());
        request.setMirrors(executionRequest.getMirrors());
        request.setProxies(executionRequest.getProxies());

        request.setOffline(session.isOffline());
        request.setLocalRepository(session.getLocalRepository());

        // project specific
        request.setRemoteRepositories(project.getRemoteArtifactRepositories());

        // important to NOT apply dep management here, leeds to unexpected side effects, e.g. when osgifier is managed in
        // project which is build. So separate deps from project which is build and build system is a good thing.
        // Note: we must explicitly set an empty map to prevent managed version resolution via maven session
        request.setManagedVersionMap(new HashMap<String, Artifact>());

        request.setArtifact(repositorySystem.createDependencyArtifact(dependency));

        final ArtifactResolutionResult result = repositorySystem.resolve(request);
        try {
            resolutionErrorHandler.throwErrors(request, result);
        } catch (ArtifactResolutionException e) {
            throw new IllegalStateException(e);
        }
        return result;
    }

    private List<ClassRealm> discoverBootExtensionClassRealms(MavenProject bootProject) {
        final List<ClassRealm> bootExtensionRelams = new ArrayList<ClassRealm>();

        final ClassRealm projectRealm = bootProject.getClassRealm();
        if (projectRealm != null) {
            @SuppressWarnings("unchecked")
            final Collection<ClassRealm> importRealms = projectRealm.getImportRealms();
            for (ClassRealm classRealm : importRealms) {
                if (isBootExtensionClassRealm(extensionRealmPrefixes, classRealm)) {
                    bootExtensionRelams.add(classRealm);
                }
            }
        }

        return bootExtensionRelams;
    }

    private static boolean isBootExtensionClassRealm(Collection<String> extensionRealmPrefixes, ClassRealm realm) {
        for (String realmPrefix : extensionRealmPrefixes) {
            if (realm.getId().startsWith(realmPrefix)) {
                return true;
            }
        }
        return false;
    }

    private List<MavenProject> buildBootstrapProjects(MavenSession session, Collection<File> descriptors) {
        final ProjectBuildingRequest request = new DefaultProjectBuildingRequest(
                session.getProjectBuildingRequest());
        request.setRemoteRepositories(filterArtifactRepositories(request.getRemoteRepositories()));

        final List<ProjectBuildingResult> results;
        try {
            results = projectBuilder.build(new ArrayList<File>(descriptors), false, request);
        } catch (ProjectBuildingException e) {
            throw new IllegalStateException("Cannot build bootstrapper project for " + e.getPomFile(), e);
        }

        final List<MavenProject> projects = new ArrayList<MavenProject>(results.size());
        for (ProjectBuildingResult result : results) {
            final MavenProject project = result.getProject();
            project.setRemoteArtifactRepositories(
                    filterArtifactRepositories(project.getRemoteArtifactRepositories()));
            project.setPluginArtifactRepositories(
                    filterArtifactRepositories(project.getPluginArtifactRepositories()));
            projects.add(project);
        }

        final ProjectSorter projectSorter;
        try {
            // HACK: Constructor arg changed with Maven 3.2 from List to Collection which made it binary incompatible
            projectSorter = (ProjectSorter) ProjectSorter.class.getConstructors()[0].newInstance(projects);
        } catch (InstantiationException e) {
            throw new IllegalStateException(e);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        } catch (InvocationTargetException e) {
            throw new IllegalStateException(e.getTargetException());
        }

        return projectSorter.getSortedProjects();
    }

    protected abstract List<ArtifactRepository> filterArtifactRepositories(
            List<ArtifactRepository> remoteRepositories);

    private List<File> getProjectDescriptors(final MavenSession bootSession) {
        final Collection<File> descriptors = new LinkedHashSet<File>();
        final Collection<File> skippedDescriptors = new HashSet<File>();
        discoverProjectDescriptors(bootSession, descriptors, skippedDescriptors);

        final List<File> pomFiles = new ArrayList<File>();
        for (File descriptor : descriptors) {
            if (skippedDescriptors.contains(descriptor)) {
                logger.info("Skipping module descriptor " + descriptor.getPath());
                continue;
            }
            pomFiles.add(descriptor);
        }
        return pomFiles;
    }

    protected abstract void discoverProjectDescriptors(MavenSession session, Collection<File> descriptors,
            Collection<File> skippedDescriptors);

    private final static class ImportEnforcer implements ClassWorldListener {
        private final ClassLoader classLoader;

        private final Set<String> extensionRealmPrefixes;

        private final List<String> imports = new ArrayList<String>();

        public ImportEnforcer(ClassLoader classLoader, Set<String> extensionRealmPrefixes, List<String> imports) {
            this.classLoader = classLoader;
            this.extensionRealmPrefixes = extensionRealmPrefixes;
            this.imports.addAll(imports);
        }

        private static String toImportPattern(Class<?> clazz) {
            final String name = clazz.getName();
            final StringBuilder sb = new StringBuilder();
            sb.append(name.substring(0, name.lastIndexOf('.')));
            sb.append(".*");
            return sb.toString();
        }

        public void realmCreated(ClassRealm realm) {
            if (isBootExtensionClassRealm(extensionRealmPrefixes, realm)) {
                addBootstrapImports(realm);
            }
        }

        private void addBootstrapImports(ClassRealm realm) {
            for (String packageImport : imports) {
                realm.importFrom(classLoader, packageImport);
            }
        }

        public void realmDisposed(ClassRealm realm) {
        }
    }
}