package hudson.maven;
/*
* Olivier Lamy
* 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.
*/
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.maven.artifact.versioning.ComparableVersion;
import org.apache.tools.ant.AntClassLoader;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.PlexusContainerException;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.util.IOUtil;
/**
* @author Olivier Lamy
*
*/
public class MavenEmbedderUtils
{
private static final String POM_PROPERTIES_PATH = "META-INF/maven/org.apache.maven/maven-core/pom.properties";
private MavenEmbedderUtils() {
// no op only to prevent construction
}
/**
* <p>
* build a {@link ClassRealm} with all jars in mavenHome/lib/*.jar
* </p>
* <p>
* the {@link ClassRealm} is ChildFirst with the current classLoader as parent.
* </p>
* @param mavenHome cannot be <code>null</code>
* @param world can be <code>null</code>
* @return
*/
public static ClassRealm buildClassRealm(File mavenHome, ClassWorld world, ClassLoader parentClassLoader )
throws MavenEmbedderException {
if ( mavenHome == null ) {
throw new IllegalArgumentException( "mavenHome cannot be null" );
}
if ( !mavenHome.exists() ) {
throw new IllegalArgumentException( "mavenHome '" + mavenHome.getPath() + "' doesn't seem to exist on this node (or you don't have sufficient rights to access it)" );
}
// list all jar under mavenHome/lib
File libDirectory = new File( mavenHome, "lib" );
if ( !libDirectory.exists() ) {
throw new IllegalArgumentException( mavenHome.getPath() + " doesn't have a 'lib' subdirectory - thus cannot be a valid maven installation!" );
}
File[] jarFiles = libDirectory.listFiles( new FilenameFilter()
{
public boolean accept( File dir, String name ) {
return name.endsWith( ".jar" );
}
} );
AntClassLoader antClassLoader = new AntClassLoader( Thread.currentThread().getContextClassLoader(), false );
for ( File jarFile : jarFiles ) {
antClassLoader.addPathComponent( jarFile );
}
if (world == null) {
world = new ClassWorld();
}
ClassRealm classRealm = new ClassRealm( world, "plexus.core", parentClassLoader == null ? antClassLoader : parentClassLoader );
for ( File jarFile : jarFiles ) {
try {
classRealm.addURL( jarFile.toURI().toURL() );
} catch ( MalformedURLException e ) {
throw new MavenEmbedderException( e.getMessage(), e );
}
}
return classRealm;
}
public static PlexusContainer buildPlexusContainer(File mavenHome, MavenRequest mavenRequest) throws MavenEmbedderException {
ClassWorld world = new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader());
ClassRealm classRealm = MavenEmbedderUtils.buildClassRealm( mavenHome, world, Thread.currentThread().getContextClassLoader() );
DefaultContainerConfiguration conf = new DefaultContainerConfiguration();
conf.setContainerConfigurationURL( mavenRequest.getOverridingComponentsXml() )
.setRealm( classRealm )
.setClassWorld( world )
.setClassPathScanning( mavenRequest.getContainerClassPathScanning() )
.setComponentVisibility( mavenRequest.getContainerComponentVisibility() );
return buildPlexusContainer(mavenRequest,conf);
}
/**
* used by PomParser in Jenkins
* @param mavenClassLoader
* @param parent
* @param mavenRequest
* @return
* @throws MavenEmbedderException
*/
public static PlexusContainer buildPlexusContainer(ClassLoader mavenClassLoader, ClassLoader parent, MavenRequest mavenRequest) throws MavenEmbedderException {
DefaultContainerConfiguration conf = new DefaultContainerConfiguration();
conf.setAutoWiring( mavenRequest.isContainerAutoWiring() )
.setClassPathScanning( mavenRequest.getContainerClassPathScanning() )
.setComponentVisibility( mavenRequest.getContainerComponentVisibility() )
.setContainerConfigurationURL( mavenRequest.getOverridingComponentsXml() );
ClassWorld classWorld = new ClassWorld();
ClassRealm classRealm = new ClassRealm( classWorld, "maven", mavenClassLoader );
classRealm.setParentRealm( new ClassRealm( classWorld, "maven-parent",
parent == null ? Thread.currentThread().getContextClassLoader()
: parent ) );
conf.setRealm( classRealm );
conf.setClassWorld( classWorld );
return buildPlexusContainer(mavenRequest,conf);
}
private static PlexusContainer buildPlexusContainer(MavenRequest mavenRequest,ContainerConfiguration containerConfiguration )
throws MavenEmbedderException {
try
{
DefaultPlexusContainer plexusContainer = new DefaultPlexusContainer( containerConfiguration );
if (mavenRequest.getMavenLoggerManager() != null) {
plexusContainer.setLoggerManager( mavenRequest.getMavenLoggerManager() );
}
if (mavenRequest.getLoggingLevel() > 0) {
plexusContainer.getLoggerManager().setThreshold( mavenRequest.getLoggingLevel() );
}
return plexusContainer;
} catch ( PlexusContainerException e ) {
throw new MavenEmbedderException( e.getMessage(), e );
}
}
/**
* @param mavenHome
* @return the maven version
* @throws MavenEmbedderException
*/
public static MavenInformation getMavenVersion(File mavenHome) throws MavenEmbedderException {
ClassRealm realm = buildClassRealm( mavenHome, null, null );
if (debug) {
debugMavenVersion(realm);
}
ClassLoader original = Thread.currentThread().getContextClassLoader();
InputStream inputStream = null;
JarFile jarFile = null;
MavenInformation information = null;
try {
Thread.currentThread().setContextClassLoader( realm );
// TODO is this really intending to use findResource rather than getResource? Cf. https://github.com/sonatype/plexus-classworlds/pull/8
URL resource = realm.findResource( POM_PROPERTIES_PATH );
if (resource == null) {
throw new MavenEmbedderException("Couldn't find maven version information in '" + mavenHome.getPath()
+ "'. Are you sure that this is a valid maven home?");
}
URLConnection uc = resource.openConnection();
if (uc instanceof JarURLConnection) {
final JarURLConnection connection = (JarURLConnection)uc;
final String entryName = connection.getEntryName();
try {
jarFile = connection.getJarFile();
final JarEntry entry = (entryName != null && jarFile != null) ? jarFile.getJarEntry(entryName) : null;
if (entry != null) {
inputStream = jarFile.getInputStream(entry);
Properties properties = new Properties();
properties.load( inputStream );
information = new MavenInformation( properties.getProperty( "version" ) , resource.toExternalForm() );
}
} finally {
if (jarFile != null) {
jarFile.close();
}
}
}
} catch ( IOException e ) {
throw new MavenEmbedderException( e.getMessage(), e );
} finally {
IOUtil.close( inputStream );
Thread.currentThread().setContextClassLoader( original );
}
return information;
}
public static boolean isAtLeastMavenVersion(File mavenHome, String version) throws MavenEmbedderException {
ComparableVersion found = new ComparableVersion( getMavenVersion( mavenHome ).getVersion() );
ComparableVersion testedOne = new ComparableVersion( version );
return found.compareTo( testedOne ) >= 0;
}
private static void debugMavenVersion(ClassRealm realm ) {
try {
// TODO as above, consider getResources
@SuppressWarnings("unchecked")
Enumeration<URL> urls = realm.findResources( POM_PROPERTIES_PATH );
System.out.println("urls for " + POM_PROPERTIES_PATH );
while(urls.hasMoreElements()) {
System.out.println("url " + urls.nextElement().toExternalForm());
}
} catch (IOException e) {
System.out.println("Ignore IOException during searching " + POM_PROPERTIES_PATH + ":" + e.getMessage());
}
}
public static boolean debug = Boolean.getBoolean( "hudson.maven.MavenEmbedderUtils.debug" );
}