/* * Copyright 2000-2009 JetBrains s.r.o. * * 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.jetbrains.maven.embedder; import org.apache.maven.DefaultMaven; import org.apache.maven.Maven; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.handler.ArtifactHandler; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.artifact.manager.DefaultWagonManager; import org.apache.maven.artifact.manager.WagonManager; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; import org.apache.maven.artifact.repository.DefaultArtifactRepository; import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; import org.apache.maven.artifact.resolver.*; import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.ReactorManager; import org.apache.maven.extension.ExtensionManager; import org.apache.maven.model.Extension; import org.apache.maven.model.Plugin; import org.apache.maven.monitor.event.DefaultEventDispatcher; import org.apache.maven.monitor.event.DefaultEventMonitor; import org.apache.maven.monitor.event.EventDispatcher; import org.apache.maven.plugin.PluginManager; import org.apache.maven.profiles.DefaultProfileManager; import org.apache.maven.profiles.ProfileManager; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectBuilder; import org.apache.maven.project.ProjectBuilderConfiguration; import org.apache.maven.project.interpolation.ModelInterpolationException; import org.apache.maven.settings.*; import org.codehaus.classworlds.ClassWorld; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.PlexusContainerException; import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.logging.BaseLoggerManager; import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.jetbrains.annotations.NotNull; import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher; import org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; public class MavenEmbedder { private static final String PROP_MAVEN_HOME = "maven.home"; private final DefaultPlexusContainer myContainer; private final Settings mySettings; private final Logger myLogger; private final MavenEmbedderSettings myEmbedderSettings; private final ArtifactRepository myLocalRepository; private MavenEmbedder(@NotNull DefaultPlexusContainer container, @NotNull Settings settings, @NotNull Logger logger, @NotNull MavenEmbedderSettings embedderSettings) { myContainer = container; mySettings = settings; myLogger = logger; myEmbedderSettings = embedderSettings; myLocalRepository = createLocalRepository(embedderSettings); loadSettings(); } private void loadSettings() { // copied from DefaultMaven.resolveParameters // copied because using original code spoils something in the container and configuration are get messed and not picked up. WagonManager wagonManager = getComponent(WagonManager.class); wagonManager.setOnline(!mySettings.isOffline()); if (wagonManager instanceof DefaultWagonManager) { ((DefaultWagonManager)wagonManager).setHttpUserAgent("Apache-Maven/2.2"); } Proxy proxy = mySettings.getActiveProxy(); if (proxy != null && proxy.getHost() != null) { String pass = decrypt(proxy.getPassword()); wagonManager.addProxy(proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(), pass, proxy.getNonProxyHosts()); } for (Object each : mySettings.getServers()) { Server server = (Server)each; String passWord = decrypt(server.getPassword()); String passPhrase = decrypt(server.getPassphrase()); wagonManager.addAuthenticationInfo(server.getId(), server.getUsername(), passWord, server.getPrivateKey(), passPhrase); wagonManager.addPermissionInfo(server.getId(), server.getFilePermissions(), server.getDirectoryPermissions()); if (server.getConfiguration() != null) { wagonManager.addConfiguration(server.getId(), (Xpp3Dom)server.getConfiguration()); } } for (Object each : mySettings.getMirrors()) { Mirror mirror = (Mirror)each; if (mirror.getUrl() == null) continue; wagonManager.addMirror(mirror.getId(), mirror.getMirrorOf(), mirror.getUrl()); } // end copied from DefaultMaven.resolveParameters } private String decrypt(String pass) { try { pass = getComponent(SecDispatcher.class, "maven").decrypt(pass); } catch (SecDispatcherException e) { MavenEmbedderLog.LOG.warn(e); } return pass; } private ArtifactRepository createLocalRepository(MavenEmbedderSettings generalSettings) { ArtifactRepositoryLayout layout = getComponent(ArtifactRepositoryLayout.class, "default"); ArtifactRepositoryFactory factory = getComponent(ArtifactRepositoryFactory.class); String url = mySettings.getLocalRepository(); if (!url.startsWith("file:")) url = "file://" + url; ArtifactRepository localRepository = new DefaultArtifactRepository("local", url, layout); boolean snapshotPolicySet = mySettings.isOffline(); if (!snapshotPolicySet && generalSettings.getSnapshotUpdatePolicy() == MavenEmbedderSettings.UpdatePolicy.ALWAYS_UPDATE) { factory.setGlobalUpdatePolicy(ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS); } factory.setGlobalChecksumPolicy(ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN); return localRepository; } @NotNull public ArtifactRepository getLocalRepository() { return myLocalRepository; } @NotNull public File getLocalRepositoryFile() { return new File(myLocalRepository.getBasedir()); } public Settings getSettings() { return mySettings; } @NotNull public MavenExecutionResult resolveProject(@NotNull final File file, @NotNull final List<String> activeProfiles, @NotNull final List<String> inactiveProfiles) { return resolveProject(file, activeProfiles, inactiveProfiles, Collections.<ResolutionListener>emptyList()); } @NotNull public MavenExecutionResult resolveProject(@NotNull final File file, @NotNull final List<String> activeProfiles, @NotNull final List<String> inactiveProfiles, List<ResolutionListener> listeners) { MavenExecutionRequest request = createRequest(file, activeProfiles, inactiveProfiles, Collections.<String>emptyList()); ProjectBuilderConfiguration config = request.getProjectBuilderConfiguration(); request.getGlobalProfileManager().loadSettingsProfiles(mySettings); ProfileManager globalProfileManager = request.getGlobalProfileManager(); globalProfileManager.loadSettingsProfiles(request.getSettings()); List<Exception> exceptions = new ArrayList<Exception>(); MavenProject project = null; try { // copied from DefaultMavenProjectBuilder.buildWithDependencies MavenProjectBuilder builder = getComponent(MavenProjectBuilder.class); project = builder.build(new File(file.getPath()), config); builder.calculateConcreteState(project, config, false); // copied from DefaultLifecycleExecutor.execute findExtensions(project); // end copied from DefaultLifecycleExecutor.execute Artifact projectArtifact = project.getArtifact(); Map managedVersions = project.getManagedVersionMap(); ArtifactMetadataSource metadataSource = getComponent(ArtifactMetadataSource.class); project.setDependencyArtifacts(project.createArtifacts(getComponent(ArtifactFactory.class), null, null)); ArtifactResolver resolver = getComponent(ArtifactResolver.class); ArtifactResolutionResult result = resolver .resolveTransitively(project.getDependencyArtifacts(), projectArtifact, managedVersions, myLocalRepository, project.getRemoteArtifactRepositories(), metadataSource, null, listeners); project.setArtifacts(result.getArtifacts()); // end copied from DefaultMavenProjectBuilder.buildWithDependencies } catch (Exception e) { return handleException(e); } return new MavenExecutionResult(project, exceptions); } private void findExtensions(MavenProject project) { // end copied from DefaultLifecycleExecutor.findExtensions ExtensionManager extensionManager = getComponent(ExtensionManager.class); for (Object each : project.getBuildExtensions()) { try { extensionManager.addExtension((Extension)each, project, myLocalRepository); } catch (PlexusContainerException e) { MavenEmbedderLog.LOG.error(e); } catch (ArtifactResolutionException e) { MavenEmbedderLog.LOG.error(e); } catch (ArtifactNotFoundException e) { MavenEmbedderLog.LOG.error(e); } } extensionManager.registerWagons(); Map handlers = findArtifactTypeHandlers(project); getComponent(ArtifactHandlerManager.class).addHandlers(handlers); } @SuppressWarnings({"unchecked"}) private Map findArtifactTypeHandlers(MavenProject project) { // end copied from DefaultLifecycleExecutor.findExtensions Map result = new HashMap(); for (Object each : project.getBuildPlugins()) { Plugin eachPlugin = (Plugin)each; if (eachPlugin.isExtensions()) { try { PluginManager pluginManager = getComponent(PluginManager.class); pluginManager.verifyPlugin(eachPlugin, project, mySettings, myLocalRepository); result.putAll(pluginManager.getPluginComponents(eachPlugin, ArtifactHandler.ROLE)); } catch (Exception e) { MavenEmbedderLog.LOG.info(e); continue; } for (Object o : result.values()) { ArtifactHandler handler = (ArtifactHandler)o; if (project.getPackaging().equals(handler.getPackaging())) { project.getArtifact().setArtifactHandler(handler); } } } } return result; } public void resolve(@NotNull final Artifact artifact, @NotNull final List<ArtifactRepository> repos) throws ArtifactResolutionException, ArtifactNotFoundException { getComponent(ArtifactResolver.class).resolve(artifact, repos, myLocalRepository); } public Set<Artifact> resolveTransitively(@NotNull Set<Artifact> toResolve, @NotNull List<ArtifactRepository> repos) throws ArtifactResolutionException, ArtifactNotFoundException { Artifact project = getComponent(ArtifactFactory.class).createBuildArtifact("temp", "temp", "666", "pom"); return getComponent(ArtifactResolver.class) .resolveTransitively(toResolve, project, Collections.EMPTY_MAP, myLocalRepository, repos, getComponent(ArtifactMetadataSource.class)) .getArtifacts(); } @NotNull public MavenExecutionResult execute(@NotNull final File file, @NotNull final List<String> activeProfiles, @NotNull final List<String> inactiveProfiles, @NotNull final List<String> goals, @NotNull final List<String> selectedProjects, boolean alsoMake, boolean alsoMakeDependents) { try { MavenExecutionRequest request = createRequest(file, activeProfiles, inactiveProfiles, goals); if (!selectedProjects.isEmpty()) { request.setRecursive(true); request.setSelectedProjects(selectedProjects); if (alsoMake && alsoMakeDependents) { request.setMakeBehavior(ReactorManager.MAKE_BOTH_MODE); } else if (alsoMake) { request.setMakeBehavior(ReactorManager.MAKE_MODE); } else if (alsoMakeDependents) { request.setMakeBehavior(ReactorManager.MAKE_DEPENDENTS_MODE); } } Maven maven = getComponent(Maven.class); Method method = maven.getClass().getDeclaredMethod("doExecute", MavenExecutionRequest.class, EventDispatcher.class); method.setAccessible(true); ReactorManager reactor = (ReactorManager)method.invoke(maven, request, request.getEventDispatcher()); return new MavenExecutionResult(reactor.getTopLevelProject(), Collections.<Exception>emptyList()); } catch (InvocationTargetException e) { return handleException(e.getTargetException()); } catch (NoSuchMethodException e) { throw new RuntimeException(e); // should never happen } catch (IllegalAccessException e) { throw new RuntimeException(e); // should never happen } } @NotNull public MavenExecutionResult readProjectWithModules(@NotNull final File file, List<String> activeProfiles, List<String> inactiveProfiles) { MavenExecutionRequest request = createRequest(file, activeProfiles, inactiveProfiles, Collections.<String>emptyList()); request.getGlobalProfileManager().loadSettingsProfiles(mySettings); request.setRecursive(true); return readProject(request); } @NotNull private MavenExecutionResult readProject(@NotNull final MavenExecutionRequest request) { ProfileManager globalProfileManager = request.getGlobalProfileManager(); globalProfileManager.loadSettingsProfiles(request.getSettings()); MavenProject rootProject = null; final List<Exception> exceptions = new ArrayList<Exception>(); Object result = null; try { final File pomFile = new File(request.getPomFile()); if (!pomFile.exists()) { throw new FileNotFoundException("File doesn't exist: " + pomFile.getPath()); } final Method getProjectsMethod = DefaultMaven.class.getDeclaredMethod("getProjects", MavenExecutionRequest.class); getProjectsMethod.setAccessible(true); Maven maven = getComponent(Maven.class); result = getProjectsMethod.invoke(maven, request); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { return handleException(e.getTargetException()); } catch (Exception e) { return handleException(e); } if (result != null) { MavenProjectBuilder builder = getComponent(MavenProjectBuilder.class); for (Object p : (List)result) { MavenProject project = (MavenProject)p; try { builder.calculateConcreteState(project, request.getProjectBuilderConfiguration(), false); } catch (ModelInterpolationException e) { exceptions.add(e); } if (project.isExecutionRoot()) { rootProject = project; } } if (rootProject == null && exceptions.isEmpty()) { throw new RuntimeException("Could't build project for unknown reason"); } } return new MavenExecutionResult(rootProject, exceptions); } @NotNull public MavenExecutionResult readProject(@NotNull final File file, @NotNull final List<String> activeProfiles, @NotNull final List<String> inactiveProfiles) { MavenExecutionRequest request = createRequest(file, activeProfiles, inactiveProfiles, Collections.<String>emptyList()); request.getGlobalProfileManager().loadSettingsProfiles(mySettings); request.setRecursive(false); return readProject(request); } private MavenExecutionRequest createRequest(File file, List<String> activeProfiles, List<String> inactiveProfiles, List<String> goals) { Properties executionProperties = myEmbedderSettings.getProperties(); if (executionProperties == null) executionProperties = new Properties(); DefaultEventDispatcher dispatcher = new DefaultEventDispatcher(); dispatcher.addEventMonitor(new DefaultEventMonitor(myLogger)); // subclassing because in DefaultMavenExecutionRequest field isRecursive is always false MavenExecutionRequest result = new DefaultMavenExecutionRequest(myLocalRepository, mySettings, dispatcher, goals, file.getParent(), createProfileManager(activeProfiles, inactiveProfiles, executionProperties), executionProperties, new Properties(), true) { private boolean myIsRecursive; @Override public boolean isRecursive() { return myIsRecursive; } @Override public void setRecursive(final boolean recursive) { myIsRecursive = recursive; } }; result.setPomFile(file.getPath()); result.setRecursive(myEmbedderSettings.isRecursive()); return result; } private MavenExecutionResult handleException(Throwable e) { if (e instanceof RuntimeException) throw (RuntimeException)e; if (e instanceof Error) throw (Error)e; return new MavenExecutionResult(null, Collections.singletonList((Exception)e)); } private ProfileManager createProfileManager(List<String> activeProfiles, List<String> inactiveProfiles, Properties executionProperties) { ProfileManager profileManager = new DefaultProfileManager(getContainer(), executionProperties); profileManager.explicitlyActivate(activeProfiles); profileManager.explicitlyDeactivate(inactiveProfiles); return profileManager; } @SuppressWarnings({"unchecked"}) public <T> T getComponent(Class<T> clazz) { try { return (T)getContainer().lookup(clazz.getName()); } catch (ComponentLookupException e) { throw new RuntimeException(e); } } @SuppressWarnings({"unchecked"}) public <T> T getComponent(Class<T> clazz, String roleHint) { try { return (T)getContainer().lookup(clazz.getName(), roleHint); } catch (ComponentLookupException e) { throw new RuntimeException(e); } } public PlexusContainer getContainer() { return myContainer; } public void release() { releaseResolverThreadExecutor(); myContainer.dispose(); } private void releaseResolverThreadExecutor() { ArtifactResolver resolver = getComponent(ArtifactResolver.class); @SuppressWarnings({"unchecked"}) FieldAccessor pool = new FieldAccessor(DefaultArtifactResolver.class, resolver, "resolveArtifactPool"); try { final Object threadPool = pool.getField(); // an instance of a hidden copy of ThreadPoolExecutor threadPool.getClass().getMethod("shutdown").invoke(threadPool); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } @NotNull public static MavenEmbedder create(@NotNull final MavenEmbedderSettings embedderSettings) { @NotNull final Logger logger = getLogger(embedderSettings); DefaultPlexusContainer container = new DefaultPlexusContainer(); container.setClassWorld(new ClassWorld("plexus.core", embedderSettings.getClass().getClassLoader())); container.setLoggerManager(new BaseLoggerManager() { @Override protected Logger createLogger(final String s) { return logger; } }); try { container.initialize(); container.start(); } catch (PlexusContainerException e) { MavenEmbedderLog.LOG.error(e); throw new RuntimeException(e); } final PlexusComponentConfigurator configurator = embedderSettings.getConfigurator(); if (configurator != null) { configurator.configureComponents(container); } File mavenHome = embedderSettings.getMavenHome(); if (mavenHome != null) { System.setProperty(PROP_MAVEN_HOME, mavenHome.getPath()); } Settings nativeSettings = buildSettings(container, embedderSettings); return new MavenEmbedder(container, nativeSettings, logger, embedderSettings); } @NotNull private static Logger getLogger(@NotNull final MavenEmbedderSettings embedderSettings) { final Logger logger = embedderSettings.getLogger(); return logger != null ? logger : new NullMavenLogger(); } public static Settings buildSettings(PlexusContainer container, MavenEmbedderSettings embedderSettings) { File file = embedderSettings.getGlobalSettingsFile(); if (file != null) { System.setProperty(MavenSettingsBuilder.ALT_GLOBAL_SETTINGS_XML_LOCATION, file.getPath()); } Settings settings = null; try { MavenSettingsBuilder builder = (MavenSettingsBuilder)container.lookup(MavenSettingsBuilder.ROLE); File userSettingsFile = embedderSettings.getUserSettingsFile(); if (userSettingsFile != null && userSettingsFile.exists() && !userSettingsFile.isDirectory()) { settings = builder.buildSettings(userSettingsFile, false); } if (settings == null) { settings = builder.buildSettings(); } } catch (ComponentLookupException e) { MavenEmbedderLog.LOG.error(e); } catch (IOException e) { MavenEmbedderLog.LOG.warn(e); } catch (XmlPullParserException e) { MavenEmbedderLog.LOG.warn(e); } if (settings == null) { settings = new Settings(); } if (embedderSettings.getLocalRepository() != null) { settings.setLocalRepository(embedderSettings.getLocalRepository().getPath()); } if (settings.getLocalRepository() == null) { settings.setLocalRepository(System.getProperty("user.home") + "/.m2/repository"); } settings.setOffline(embedderSettings.isWorkOffline()); settings.setInteractiveMode(false); settings.setUsePluginRegistry(embedderSettings.isUsePluginRegistry()); RuntimeInfo runtimeInfo = new RuntimeInfo(settings); runtimeInfo.setPluginUpdateOverride(embedderSettings.getPluginUpdatePolicy() == MavenEmbedderSettings.UpdatePolicy.ALWAYS_UPDATE); settings.setRuntimeInfo(runtimeInfo); return settings; } public static <T> void setImplementation(PlexusContainer container, Class<T> componentClass, Class<? extends T> implementationClass) { ComponentDescriptor d = container.getComponentDescriptor(componentClass.getName()); d.setImplementation(implementationClass.getName()); } }