org.apache.maven.classrealm.DefaultClassRealmManager Maven / Gradle / Ivy
The newest version!
package org.apache.maven.classrealm;
/*
* 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.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.artifact.Artifact;
/**
* Manages the class realms used by Maven. Warning: This is an internal utility class that is only
* public for technical reasons, it is not part of the public API. In particular, this class can be changed or deleted
* without prior notice.
*
* @author Benjamin Bentmann
*/
@Component( role = ClassRealmManager.class )
public class DefaultClassRealmManager
implements ClassRealmManager
{
@Requirement
private Logger logger;
@Requirement
protected PlexusContainer container;
private ClassRealm mavenRealm;
private ClassWorld getClassWorld()
{
return ( (MutablePlexusContainer) container ).getClassWorld();
}
private ClassRealm newRealm( String id )
{
ClassWorld world = getClassWorld();
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();
}
}
}
}
public synchronized ClassRealm getMavenApiRealm()
{
if ( mavenRealm == null )
{
mavenRealm = newRealm( "maven.api" );
List constituents = new ArrayList();
List parentImports = new ArrayList();
Map foreignImports = new HashMap();
importMavenApi( foreignImports );
callDelegates( mavenRealm, RealmType.Core, mavenRealm.getParentClassLoader(), parentImports,
foreignImports, constituents );
wireRealm( mavenRealm, parentImports, foreignImports );
populateRealm( mavenRealm, constituents );
}
return mavenRealm;
}
private void importMavenApi( Map imports )
{
ClassRealm coreRealm = getCoreRealm();
// maven-*
imports.put( "org.apache.maven.*", coreRealm );
imports.put( "org.apache.maven.artifact", coreRealm );
imports.put( "org.apache.maven.classrealm", coreRealm );
imports.put( "org.apache.maven.cli", coreRealm );
imports.put( "org.apache.maven.configuration", coreRealm );
imports.put( "org.apache.maven.exception", coreRealm );
imports.put( "org.apache.maven.execution", coreRealm );
imports.put( "org.apache.maven.lifecycle", coreRealm );
imports.put( "org.apache.maven.model", coreRealm );
imports.put( "org.apache.maven.monitor", coreRealm );
imports.put( "org.apache.maven.plugin", coreRealm );
imports.put( "org.apache.maven.profiles", coreRealm );
imports.put( "org.apache.maven.project", coreRealm );
imports.put( "org.apache.maven.reporting", coreRealm );
imports.put( "org.apache.maven.repository", coreRealm );
imports.put( "org.apache.maven.rtinfo", coreRealm );
imports.put( "org.apache.maven.settings", coreRealm );
imports.put( "org.apache.maven.toolchain", coreRealm );
imports.put( "org.apache.maven.usability", coreRealm );
// wagon-api
imports.put( "org.apache.maven.wagon.*", coreRealm );
imports.put( "org.apache.maven.wagon.authentication", coreRealm );
imports.put( "org.apache.maven.wagon.authorization", coreRealm );
imports.put( "org.apache.maven.wagon.events", coreRealm );
imports.put( "org.apache.maven.wagon.observers", coreRealm );
imports.put( "org.apache.maven.wagon.proxy", coreRealm );
imports.put( "org.apache.maven.wagon.repository", coreRealm );
imports.put( "org.apache.maven.wagon.resource", coreRealm );
// aether-api, aether-spi, aether-impl
imports.put( "org.eclipse.aether.*", coreRealm );
imports.put( "org.eclipse.aether.artifact", coreRealm );
imports.put( "org.eclipse.aether.collection", coreRealm );
imports.put( "org.eclipse.aether.deployment", coreRealm );
imports.put( "org.eclipse.aether.graph", coreRealm );
imports.put( "org.eclipse.aether.impl", coreRealm );
imports.put( "org.eclipse.aether.installation", coreRealm );
imports.put( "org.eclipse.aether.metadata", coreRealm );
imports.put( "org.eclipse.aether.repository", coreRealm );
imports.put( "org.eclipse.aether.resolution", coreRealm );
imports.put( "org.eclipse.aether.spi", coreRealm );
imports.put( "org.eclipse.aether.transfer", coreRealm );
imports.put( "org.eclipse.aether.version", coreRealm );
// plexus-classworlds
imports.put( "org.codehaus.plexus.classworlds", coreRealm );
// classworlds (for legacy code)
imports.put( "org.codehaus.classworlds", coreRealm );
// plexus-utils (for DOM-type fields in maven-model)
imports.put( "org.codehaus.plexus.util.xml.Xpp3Dom", coreRealm );
imports.put( "org.codehaus.plexus.util.xml.pull.XmlPullParser", coreRealm );
imports.put( "org.codehaus.plexus.util.xml.pull.XmlPullParserException", coreRealm );
imports.put( "org.codehaus.plexus.util.xml.pull.XmlSerializer", coreRealm );
// plexus-container, plexus-component-annotations
imports.put( "org.codehaus.plexus.*", coreRealm );
imports.put( "org.codehaus.plexus.component", coreRealm );
imports.put( "org.codehaus.plexus.configuration", coreRealm );
imports.put( "org.codehaus.plexus.container", coreRealm );
imports.put( "org.codehaus.plexus.context", coreRealm );
imports.put( "org.codehaus.plexus.lifecycle", coreRealm );
imports.put( "org.codehaus.plexus.logging", coreRealm );
imports.put( "org.codehaus.plexus.personality", coreRealm );
// javax.inject (JSR-330)
imports.put( "javax.inject.*", coreRealm );
imports.put( "javax.enterprise.inject.*", coreRealm );
// com.google
//
// We may potentially want to export these, but right now I'm not sure that anything Guice specific needs
// to be made available to plugin authors. If we find people are getting fancy and want to take advantage
// of Guice specifics we can expose that later. Really some testing needs to be done to see full hiding
// of Guice has any impact on what we may categorize as a standard JSR-330 based Tesla/Maven plugin.
//
// imports.put( "com.google.inject.*", coreRealm );
// imports.put( "com.google.inject.binder.*", coreRealm );
// imports.put( "com.google.inject.matcher.*", coreRealm );
// imports.put( "com.google.inject.name.*", coreRealm );
// imports.put( "com.google.inject.spi.*", coreRealm );
// imports.put( "com.google.inject.util.*", coreRealm );
// SLF4J
imports.put( "org.slf4j.*", coreRealm );
}
/**
* Creates a new class realm with the specified parent and imports.
*
* @param baseRealmId The base id to use for the new realm, must not be {@code null}.
* @param type The type of the class realm, must not be {@code null}.
* @param parent The parent realm for the new realm, may be {@code null}.
* @param parentImports The packages/types to import from the parent realm, may be {@code null}.
* @param foreignImports The packages/types to import from foreign realms, may be {@code null}.
* @param artifacts The artifacts to add to the realm, may be {@code null}. Unresolved artifacts (i.e. with a
* missing file) will automatically be excluded from the realm.
* @return The created class realm, never {@code null}.
*/
private ClassRealm createRealm( String baseRealmId, RealmType type, ClassLoader parent, List parentImports,
Map foreignImports, List artifacts )
{
Set artifactIds = new LinkedHashSet();
List constituents = new ArrayList();
if ( artifacts != null )
{
for ( Artifact artifact : artifacts )
{
artifactIds.add( getId( artifact ) );
if ( artifact.getFile() != null )
{
constituents.add( new ArtifactClassRealmConstituent( artifact ) );
}
}
}
if ( parentImports != null )
{
parentImports = new ArrayList( parentImports );
}
else
{
parentImports = new ArrayList();
}
if ( foreignImports != null )
{
foreignImports = new TreeMap( foreignImports );
}
else
{
foreignImports = new TreeMap();
}
ClassRealm classRealm = newRealm( baseRealmId );
if ( parent != null )
{
classRealm.setParentClassLoader( parent );
}
callDelegates( classRealm, type, parent, parentImports, foreignImports, constituents );
wireRealm( classRealm, parentImports, foreignImports );
Set includedIds = populateRealm( classRealm, constituents );
if ( logger.isDebugEnabled() )
{
artifactIds.removeAll( includedIds );
for ( String id : artifactIds )
{
logger.debug( " Excluded: " + id );
}
}
return classRealm;
}
public ClassRealm getCoreRealm()
{
return container.getContainerRealm();
}
public ClassRealm createProjectRealm( Model model, List artifacts )
{
if ( model == null )
{
throw new IllegalArgumentException( "model missing" );
}
ClassLoader parent = getMavenApiRealm();
return createRealm( getKey( model ), RealmType.Project, parent, null, null, artifacts );
}
private static String getKey( Model model )
{
return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
}
public ClassRealm createExtensionRealm( Plugin plugin, List artifacts )
{
if ( plugin == null )
{
throw new IllegalArgumentException( "extension plugin missing" );
}
ClassLoader parent = ClassLoader.getSystemClassLoader();
Map foreignImports =
Collections. singletonMap( "", getMavenApiRealm() );
return createRealm( getKey( plugin, true ), RealmType.Extension, parent, null, foreignImports, artifacts );
}
public ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List parentImports,
Map foreignImports, List artifacts )
{
if ( plugin == null )
{
throw new IllegalArgumentException( "plugin missing" );
}
if ( parent == null )
{
parent = ClassLoader.getSystemClassLoader();
}
return createRealm( getKey( plugin, false ), RealmType.Plugin, parent, parentImports, foreignImports, artifacts );
}
private static String getKey( Plugin plugin, boolean extension )
{
String version = ArtifactUtils.toSnapshotVersion( plugin.getVersion() );
return ( extension ? "extension>" : "plugin>" ) + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":"
+ version;
}
private static String getId( Artifact artifact )
{
return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
artifact.getClassifier(), artifact.getBaseVersion() );
}
private static String getId( ClassRealmConstituent constituent )
{
return getId( constituent.getGroupId(), constituent.getArtifactId(), constituent.getType(),
constituent.getClassifier(), constituent.getVersion() );
}
private static String getId( String gid, String aid, String type, String cls, String ver )
{
return gid + ':' + aid + ':' + type + ( StringUtils.isNotEmpty( cls ) ? ':' + cls : "" ) + ':' + ver;
}
private List getDelegates()
{
try
{
return container.lookupList( ClassRealmManagerDelegate.class );
}
catch ( ComponentLookupException e )
{
logger.error( "Failed to lookup class realm delegates: " + e.getMessage(), e );
return Collections.emptyList();
}
}
private void callDelegates( ClassRealm classRealm, RealmType type, ClassLoader parent, List parentImports,
Map foreignImports, List constituents )
{
List delegates = getDelegates();
if ( !delegates.isEmpty() )
{
ClassRealmRequest request =
new DefaultClassRealmRequest( type, parent, parentImports, foreignImports, constituents );
for ( ClassRealmManagerDelegate delegate : delegates )
{
try
{
delegate.setupRealm( classRealm, request );
}
catch ( Exception e )
{
logger.error( delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": "
+ e.getMessage(), e );
}
}
}
}
private Set populateRealm( ClassRealm classRealm, List constituents )
{
Set includedIds = new LinkedHashSet();
if ( logger.isDebugEnabled() )
{
logger.debug( "Populating class realm " + classRealm.getId() );
}
for ( ClassRealmConstituent constituent : constituents )
{
File file = constituent.getFile();
String id = getId( constituent );
includedIds.add( id );
if ( logger.isDebugEnabled() )
{
logger.debug( " Included: " + id );
}
try
{
classRealm.addURL( file.toURI().toURL() );
}
catch ( MalformedURLException e )
{
// Not going to happen
logger.error( e.getMessage(), e );
}
}
return includedIds;
}
private void wireRealm( ClassRealm classRealm, List parentImports, Map foreignImports )
{
if ( foreignImports != null && !foreignImports.isEmpty() )
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Importing foreign packages into class realm " + classRealm.getId() );
}
for ( Map.Entry entry : foreignImports.entrySet() )
{
ClassLoader importedRealm = entry.getValue();
String imp = entry.getKey();
if ( logger.isDebugEnabled() )
{
logger.debug( " Imported: " + imp + " < " + getId( importedRealm ) );
}
classRealm.importFrom( importedRealm, imp );
}
}
if ( parentImports != null && !parentImports.isEmpty() )
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Importing parent packages into class realm " + classRealm.getId() );
}
for ( String imp : parentImports )
{
if ( logger.isDebugEnabled() )
{
logger.debug( " Imported: " + imp + " < " + getId( classRealm.getParentClassLoader() ) );
}
classRealm.importFromParent( imp );
}
}
}
private String getId( ClassLoader classLoader )
{
if ( classLoader instanceof ClassRealm )
{
return ( (ClassRealm) classLoader ).getId();
}
return String.valueOf( classLoader );
}
}