blob: e9a0727b6c29885fc3ed170a3c7f67951499536d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.mae.boot.embed;
import static org.apache.maven.mae.conf.MAELibraries.loadLibraries;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Level;
import org.apache.maven.Maven;
import org.apache.maven.cli.MavenLoggerManager;
import org.apache.maven.cli.PrintStreamLogger;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.mae.boot.services.MAEServiceManager;
import org.apache.maven.mae.conf.CoreLibrary;
import org.apache.maven.mae.conf.MAEConfiguration;
import org.apache.maven.mae.conf.MAELibrary;
import org.apache.maven.mae.conf.loader.MAELibraryLoader;
import org.apache.maven.mae.conf.loader.ServiceLibraryLoader;
import org.apache.maven.mae.internal.container.ComponentKey;
import org.apache.maven.mae.internal.container.ComponentSelector;
import org.apache.maven.mae.internal.container.InstanceRegistry;
import org.apache.maven.mae.internal.container.MAEContainer;
import org.apache.maven.mae.internal.container.VirtualInstance;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.settings.building.SettingsBuilder;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.PlexusContainerException;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
import org.sonatype.plexus.components.sec.dispatcher.SecDispatcher;
public class MAEEmbedderBuilder
{
private static final MAELibraryLoader CORE_LOADER = new MAELibraryLoader()
{
@Override
public Collection<? extends MAELibrary> loadLibraries( final MAEConfiguration embConfig )
throws IOException
{
return Collections.singleton( new CoreLibrary() );
}
};
private boolean showErrors = false;
private boolean quiet = false;
private boolean debug = false;
private boolean showVersion = false;
private PrintStream stdout = System.out;
private PrintStream stderr = System.err;
private InputStream stdin = System.in;
private Logger logger;
private File logFile;
private MAEConfiguration config;
private ClassWorld classWorld;
private ClassLoader coreClassLoader;
private Maven maven;
private ModelProcessor modelProcessor;
private MAEContainer container;
private MavenExecutionRequestPopulator executionRequestPopulator;
private SettingsBuilder settingsBuilder;
private DefaultSecDispatcher securityDispatcher;
private MAEServiceManager serviceManager;
private transient String mavenHome;
private transient boolean loggerAutoCreated = false;
private MAEEmbedder embedder;
private String[] debugLogHandles;
private boolean modelProcessorProvided;
private boolean mavenProvided;
private boolean executionRequestPopulatorProvided;
private boolean settingsBuilderProvided;
private boolean securityDispatcherProvided;
private boolean serviceManagerProvided;
private boolean logHandlesConfigured;
private boolean configProvided;
private ContainerConfiguration containerConfiguration;
private boolean classScanningEnabled;
private List<MAELibraryLoader> libraryLoaders;
private final VirtualInstance<MAEEmbedder> embedderVirtual =
new VirtualInstance<MAEEmbedder>( MAEEmbedder.class );
public synchronized MAEEmbedderBuilder withSettingsBuilder( final SettingsBuilder settingsBuilder )
{
this.settingsBuilder = settingsBuilder;
settingsBuilderProvided = true;
return this;
}
public synchronized SettingsBuilder settingsBuilder()
throws MAEEmbeddingException
{
if ( settingsBuilder == null )
{
settingsBuilder = lookup( SettingsBuilder.class );
settingsBuilderProvided = false;
}
return settingsBuilder;
}
public synchronized MAEEmbedderBuilder withSecurityDispatcher( final DefaultSecDispatcher securityDispatcher )
{
this.securityDispatcher = securityDispatcher;
securityDispatcherProvided = true;
return this;
}
public synchronized DefaultSecDispatcher securityDispatcher()
throws MAEEmbeddingException
{
if ( securityDispatcher == null )
{
securityDispatcher = (DefaultSecDispatcher) lookup( SecDispatcher.class, "maven" );
securityDispatcherProvided = false;
}
return securityDispatcher;
}
public synchronized MAEEmbedderBuilder withServiceManager( final MAEServiceManager serviceManager )
{
this.serviceManager = serviceManager;
serviceManagerProvided = true;
return this;
}
public synchronized MAEServiceManager serviceManager()
throws MAEEmbeddingException
{
if ( serviceManager == null )
{
serviceManager = lookup( MAEServiceManager.class );
serviceManagerProvided = true;
}
return serviceManager;
}
public synchronized MAEEmbedderBuilder withExecutionRequestPopulator( final MavenExecutionRequestPopulator executionRequestPopulator )
{
this.executionRequestPopulator = executionRequestPopulator;
executionRequestPopulatorProvided = true;
return this;
}
public synchronized MavenExecutionRequestPopulator executionRequestPopulator()
throws MAEEmbeddingException
{
if ( executionRequestPopulator == null )
{
executionRequestPopulator = lookup( MavenExecutionRequestPopulator.class );
executionRequestPopulatorProvided = false;
}
return executionRequestPopulator;
}
public synchronized MAEEmbedderBuilder withCoreClassLoader( final ClassLoader classLoader )
{
coreClassLoader = classLoader;
return this;
}
public synchronized MAEEmbedderBuilder withCoreClassLoader( final ClassLoader root,
final Object... constituents )
throws MalformedURLException
{
if ( constituents != null && constituents.length > 0 )
{
final Set<URL> urls = new LinkedHashSet<URL>();
for ( final Object object : constituents )
{
if ( object instanceof URL )
{
urls.add( (URL) object );
}
else if ( object instanceof CharSequence )
{
urls.add( new URL( object.toString() ) );
}
else if ( object instanceof File )
{
urls.add( ( (File) object ).toURI().toURL() );
}
else
{
String fname;
ClassLoader cloader;
if ( object instanceof Class<?> )
{
fname = ( (Class<?>) object ).getName();
cloader = ( (Class<?>) object ).getClassLoader();
}
else
{
fname = object.getClass().getName();
cloader = object.getClass().getClassLoader();
}
fname = "/" + fname.replace( '.', '/' ) + ".class";
final URL resource = cloader.getResource( fname );
if ( resource == null )
{
throw new IllegalStateException(
"Class doesn't appear in its own classloader! ["
+ object.getClass().getName() + "]" );
}
String path = resource.toExternalForm();
if ( path.startsWith( "jar:" ) )
{
path = path.substring( "jar:".length() );
}
final int idx = path.indexOf( '!' );
if ( idx > -1 )
{
path = path.substring( 0, idx );
}
urls.add( new URL( path ) );
}
}
coreClassLoader = new URLClassLoader( urls.toArray( new URL[] {} ), root );
}
else
{
coreClassLoader = root;
}
return this;
}
public synchronized MAEEmbedderBuilder withClassWorld( final ClassWorld classWorld )
{
this.classWorld = classWorld;
return this;
}
public synchronized ClassLoader coreClassLoader()
{
if ( coreClassLoader == null )
{
coreClassLoader = Thread.currentThread().getContextClassLoader();
}
return coreClassLoader;
}
public synchronized ClassWorld classWorld()
{
if ( classWorld == null )
{
classWorld = new ClassWorld( "plexus.core", coreClassLoader() );
}
return classWorld;
}
public synchronized MAEEmbedderBuilder withContainerConfiguration( final ContainerConfiguration containerConfiguration )
{
this.containerConfiguration = containerConfiguration;
return this;
}
public synchronized ContainerConfiguration containerConfiguration()
{
if ( containerConfiguration == null )
{
containerConfiguration =
new DefaultContainerConfiguration().setClassWorld( classWorld() ).setName( "maven" ).setClassPathScanning( classScanningEnabled ? "ON"
: "OFF" );
}
return containerConfiguration;
}
public synchronized MAEEmbedderBuilder withClassScanningEnabled( final boolean classScanningEnabled )
{
this.classScanningEnabled = classScanningEnabled;
return this;
}
public boolean isClassScanningEnabled()
{
return classScanningEnabled;
}
public synchronized MAEEmbedderBuilder withMaven( final Maven maven )
{
this.maven = maven;
mavenProvided = true;
return this;
}
public synchronized Maven maven()
throws MAEEmbeddingException
{
if ( maven == null )
{
maven = lookup( Maven.class );
mavenProvided = false;
}
return maven;
}
public synchronized MAEEmbedderBuilder withModelProcessor( final ModelProcessor modelProcessor )
{
this.modelProcessor = modelProcessor;
modelProcessorProvided = true;
return this;
}
public synchronized ModelProcessor modelProcessor()
throws MAEEmbeddingException
{
if ( modelProcessor == null )
{
modelProcessor = lookup( ModelProcessor.class );
modelProcessorProvided = false;
}
return modelProcessor;
}
private <T> T lookup( final Class<T> cls )
throws MAEEmbeddingException
{
try
{
return container().lookup( cls );
}
catch ( final ComponentLookupException e )
{
throw new MAEEmbeddingException( "Failed to lookup component: %s. Reason: %s", e,
cls.getName(), e.getMessage() );
}
}
private <T> T lookup( final Class<T> cls, final String hint )
throws MAEEmbeddingException
{
try
{
return container().lookup( cls, hint );
}
catch ( final ComponentLookupException e )
{
throw new MAEEmbeddingException(
"Failed to lookup component: {0} with hint: {1}. Reason: {2}",
e, cls.getName(), hint, e.getMessage() );
}
}
public synchronized MAEEmbedderBuilder withContainer( final MAEContainer container )
{
this.container = container;
resetContainer();
return this;
}
public synchronized void resetContainer()
{
if ( !modelProcessorProvided )
{
modelProcessor = null;
}
if ( !executionRequestPopulatorProvided )
{
executionRequestPopulator = null;
}
if ( !settingsBuilderProvided )
{
settingsBuilder = null;
}
if ( !securityDispatcherProvided )
{
securityDispatcher = null;
}
if ( !serviceManagerProvided )
{
serviceManager = null;
}
if ( !mavenProvided )
{
maven = null;
}
if ( !configProvided )
{
config = null;
}
if ( container != null )
{
container = null;
}
}
public synchronized MAEContainer container()
throws MAEEmbeddingException
{
// Need to switch to using: org.codehaus.plexus.MutablePlexusContainer.addPlexusInjector(List<PlexusBeanModule>,
// Module...)
if ( container == null )
{
final ContainerConfiguration cc = containerConfiguration();
final InstanceRegistry reg = new InstanceRegistry( instanceRegistry() );
reg.addVirtual( new ComponentKey<MAEEmbedder>( MAEEmbedder.class ), embedderVirtual );
MAEContainer c;
try
{
c = new MAEContainer( cc, selector(), reg );
}
catch ( final PlexusContainerException e )
{
throw new MAEEmbeddingException( "Failed to initialize component container: {0}",
e, e.getMessage() );
}
c.setLoggerManager( new MavenLoggerManager( logger ) );
container = c;
}
return container;
}
public synchronized ComponentSelector selector()
{
return configuration().getComponentSelector();
}
public synchronized InstanceRegistry instanceRegistry()
{
return configuration().getInstanceRegistry();
}
public MAEEmbedderBuilder withConfiguration( final MAEConfiguration config )
{
this.config = config;
configProvided = true;
return this;
}
public synchronized MAEConfiguration configuration()
{
final String[] debugLogHandles = debugLogHandles();
if ( !logHandlesConfigured && debugLogHandles != null )
{
for ( final String logHandle : debugLogHandles )
{
final org.apache.log4j.Logger logger =
org.apache.log4j.Logger.getLogger( logHandle );
logger.setLevel( Level.DEBUG );
}
logHandlesConfigured = true;
}
if ( config == null )
{
config = new MAEConfiguration();
if ( shouldShowDebug() )
{
config.withDebug();
}
else
{
config.withoutDebug();
}
try
{
final List<MAELibraryLoader> loaders = libraryLoaders();
final Collection<MAELibrary> libraries = loadLibraries( config, loaders );
config.withLibraries( libraries );
if ( debugLogHandles != null
&& Arrays.binarySearch( debugLogHandles,
MAEConfiguration.STANDARD_LOG_HANDLE_CORE ) > -1 )
{
MAEEmbedder.showInfo( config, loaders, standardOut() );
}
}
catch ( final IOException e )
{
logger.error( "Failed to query context classloader for component-overrides files. Reason: "
+ e.getMessage(), e );
}
configProvided = false;
}
return config;
}
public MAEEmbedderBuilder withServiceLibraryLoader( final boolean enabled )
{
boolean found = false;
final Collection<? extends MAELibraryLoader> loaders = libraryLoadersInternal();
for ( final MAELibraryLoader loader : new LinkedHashSet<MAELibraryLoader>( loaders ) )
{
if ( loader instanceof ServiceLibraryLoader )
{
found = true;
if ( !enabled )
{
loaders.remove( loader );
}
else
{
break;
}
}
}
if ( enabled && !found )
{
withLibraryLoader( new ServiceLibraryLoader() );
}
return this;
}
public boolean isServiceLibraryLoaderUsed()
{
for ( final MAELibraryLoader loader : libraryLoadersInternal() )
{
if ( loader instanceof ServiceLibraryLoader )
{
return true;
}
}
return false;
}
public MAEEmbedderBuilder withLibraryLoader( final MAELibraryLoader loader )
{
libraryLoadersInternal().add( loader );
return this;
}
public MAEEmbedderBuilder withLibraryLoader( final MAELibraryLoader loader, final int offset )
{
final List<MAELibraryLoader> loaders = libraryLoadersInternal();
if ( offset < 0 )
{
// idx is negative, so we can add to the size here to get the insert index.
loaders.add( loaders.size() + offset, loader );
}
else
{
loaders.add( offset, loader );
}
return this;
}
private List<MAELibraryLoader> libraryLoadersInternal()
{
if ( libraryLoaders == null )
{
libraryLoaders =
new ArrayList<MAELibraryLoader>(
Collections.singletonList( new ServiceLibraryLoader() ) );
}
return libraryLoaders;
}
public List<MAELibraryLoader> libraryLoaders()
{
final List<MAELibraryLoader> loaders = libraryLoadersInternal();
if ( !loaders.isEmpty() && CORE_LOADER != loaders.get( 0 ) )
{
loaders.remove( CORE_LOADER );
}
loaders.add( 0, CORE_LOADER );
return loaders;
}
public MAEEmbedderBuilder withVersion( final boolean showVersion )
{
this.showVersion = showVersion;
return this;
}
public boolean showVersion()
{
return showVersion;
}
public MAEEmbedderBuilder withLogFile( final File logFile )
{
this.logFile = logFile;
return this;
}
public File logFile()
{
return logFile;
}
public MAEEmbedderBuilder withQuietMode( final boolean quiet )
{
this.quiet = quiet;
return this;
}
public boolean shouldBeQuiet()
{
return quiet;
}
public MAEEmbedderBuilder withDebugMode( final boolean debug )
{
this.debug = debug;
return this;
}
public boolean shouldShowDebug()
{
return debug;
}
public MAEEmbedderBuilder withErrorMode( final boolean showErrors )
{
this.showErrors = showErrors;
return this;
}
public boolean shouldShowErrors()
{
return showErrors;
}
public synchronized MAEEmbedderBuilder withStandardOut( final PrintStream stdout )
{
this.stdout = stdout;
if ( loggerAutoCreated )
{
logger = null;
}
return this;
}
public PrintStream standardOut()
{
return stdout;
}
public MAEEmbedderBuilder withStandardErr( final PrintStream stderr )
{
this.stderr = stderr;
return this;
}
public PrintStream standardErr()
{
return stderr;
}
public MAEEmbedderBuilder withStandardIn( final InputStream stdin )
{
this.stdin = stdin;
return this;
}
public InputStream standardIn()
{
return stdin;
}
public MAEEmbedderBuilder withLogger( final Logger logger )
{
this.logger = logger;
return this;
}
public synchronized Logger logger()
{
if ( logger == null )
{
logger = new PrintStreamLogger( stdout );
loggerAutoCreated = true;
}
return logger;
}
public synchronized String mavenHome()
{
if ( mavenHome == null )
{
String mavenHome = System.getProperty( "maven.home" );
if ( mavenHome != null )
{
try
{
mavenHome = new File( mavenHome ).getCanonicalPath();
}
catch ( final IOException e )
{
mavenHome = new File( mavenHome ).getAbsolutePath();
}
System.setProperty( "maven.home", mavenHome );
this.mavenHome = mavenHome;
}
}
return mavenHome;
}
protected synchronized void wireLogging()
{
if ( logFile() != null )
{
try
{
final PrintStream newOut = new PrintStream( logFile );
withStandardOut( newOut );
}
catch ( final FileNotFoundException e )
{}
}
logger();
}
protected synchronized MAEEmbedder createEmbedder()
throws MAEEmbeddingException
{
final MAEEmbedder embedder =
new MAEEmbedder( maven(), configuration(), container(), settingsBuilder(),
executionRequestPopulator(), securityDispatcher(), serviceManager(),
libraryLoaders(), standardOut(), logger(), shouldShowErrors(),
showVersion() );
embedderVirtual.setInstance( embedder );
return embedder;
}
public synchronized MAEEmbedder build()
throws MAEEmbeddingException
{
if ( embedder == null )
{
logger();
configuration();
mavenHome();
wireLogging();
embedder = createEmbedder();
}
return embedder;
}
public MAEEmbedderBuilder withDebugLogHandles( final String[] debugLogHandles )
{
this.debugLogHandles = debugLogHandles;
logHandlesConfigured = false;
return this;
}
public String[] debugLogHandles()
{
return debugLogHandles;
}
}