org.codehaus.plexus.languages.java.jpms.LocationManager Maven / Gradle / Ivy
package org.codehaus.plexus.languages.java.jpms;
import java.io.File;
/*
* 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.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.inject.Named;
import javax.inject.Singleton;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaProvides;
/**
* Maps artifacts to modules and analyzes the type of required modules
*
* @author Robert Scholte
* @since 1.0.0
*/
@Named
@Singleton
public class LocationManager
{
private ModuleInfoParser binaryParser;
private SourceModuleInfoParser sourceParser;
private ManifestModuleNameExtractor manifestModuleNameExtractor;
public LocationManager()
{
this.binaryParser = new BinaryModuleInfoParser();
this.sourceParser = new SourceModuleInfoParser();
this.manifestModuleNameExtractor = new ManifestModuleNameExtractor();
}
LocationManager( ModuleInfoParser binaryParser, SourceModuleInfoParser sourceParser )
{
this.binaryParser = binaryParser;
this.sourceParser = sourceParser;
this.manifestModuleNameExtractor = new ManifestModuleNameExtractor();
}
/**
* @param descriptorPath never {@code null}
* @return the parsed module descriptor
* @throws IOException when descriptorPath could not be read
*/
public ResolvePathResult parseModuleDescriptor( Path descriptorPath ) throws IOException
{
JavaModuleDescriptor moduleDescriptor;
if ( descriptorPath.endsWith( "module-info.java" ) )
{
moduleDescriptor = sourceParser.fromSourcePath( descriptorPath );
}
else
{
throw new IOException( "Invalid path to module descriptor: " + descriptorPath );
}
return new ResolvePathResult().setModuleDescriptor( moduleDescriptor)
.setModuleNameSource( ModuleNameSource.MODULEDESCRIPTOR );
}
/**
* @param descriptorPath never {@code null}
* @return the parsed module descriptor
* @throws IOException when descriptorPath could not be read
*/
public ResolvePathResult parseModuleDescriptor( File descriptorPath ) throws IOException
{
return parseModuleDescriptor( descriptorPath.toPath() );
}
/**
* @param descriptorPath never {@code null}
* @return the parsed module descriptor
* @throws IOException when descriptorPath could not be read
*/
public ResolvePathResult parseModuleDescriptor( String descriptorPath ) throws IOException
{
return parseModuleDescriptor( Paths.get( descriptorPath ) );
}
/**
* Resolve a single jar
*
* @param request the request
* @return the {@link ResolvePathResult}, containing the name and optional module descriptor
* @throws IOException if any occurs
*/
public ResolvePathResult resolvePath( final ResolvePathRequest request ) throws IOException
{
ModuleNameExtractor filenameExtractor = new ModuleNameExtractor()
{
MainClassModuleNameExtractor extractor = new MainClassModuleNameExtractor( request.getJdkHome() );
@Override
public String extract( Path file )
throws IOException
{
if ( request.getJdkHome() != null )
{
return extractor.extract( Collections.singletonMap( file, file ) ).get( file );
}
else
{
return CmdModuleNameExtractor.getModuleName( file );
}
}
};
return resolvePath( request.toPath( request.getPathElement() ), filenameExtractor );
}
/**
* Decide for every {@code request.getPathElements()} if it belongs to the modulePath or classPath, based on the
* {@code request.getMainModuleDescriptor()}.
*
* @param request the paths to resolve
* @return the result of the resolution
* @throws IOException if a critical IOException occurs
*/
public ResolvePathsResult resolvePaths( final ResolvePathsRequest request )
throws IOException
{
final ResolvePathsResult result = request.createResult();
Map pathElements = new LinkedHashMap<>( request.getPathElements().size() );
JavaModuleDescriptor mainModuleDescriptor = getMainModuleDescriptor( request );
result.setMainModuleDescriptor( mainModuleDescriptor );
// key = service, value = names of modules that provide this service
Map> availableProviders = new HashMap<>();
if( mainModuleDescriptor != null && request.isIncludeAllProviders() )
{
collectProviders( mainModuleDescriptor, availableProviders );
}
Map availableNamedModules = new HashMap<>();
Map moduleNameSources = new HashMap<>();
final Map filenameAutoModules = new HashMap<>();
// collect all modules from path
for ( final T t : request.getPathElements() )
{
JavaModuleDescriptor moduleDescriptor;
ModuleNameSource source;
ModuleNameExtractor nameExtractor = new ModuleNameExtractor()
{
@Override
public String extract( Path path )
throws IOException
{
if ( request.getJdkHome() != null )
{
filenameAutoModules.put( t, path );
}
else
{
return CmdModuleNameExtractor.getModuleName( path );
}
return null;
}
};
try
{
ResolvePathResult resolvedPath = resolvePath( request.toPath( t ), nameExtractor );
moduleDescriptor = resolvedPath.getModuleDescriptor();
source = resolvedPath.getModuleNameSource();
}
catch ( Exception e )
{
result.getPathExceptions().put( t, e );
continue;
}
if ( moduleDescriptor != null )
{
moduleNameSources.put( moduleDescriptor.name(), source );
availableNamedModules.put( moduleDescriptor.name(), moduleDescriptor );
if ( request.isIncludeAllProviders() )
{
collectProviders( moduleDescriptor, availableProviders );
}
}
pathElements.put( t, moduleDescriptor );
}
result.setPathElements( pathElements );
if ( !filenameAutoModules.isEmpty() )
{
MainClassModuleNameExtractor extractor = new MainClassModuleNameExtractor( request.getJdkHome() );
Map automodules = extractor.extract( filenameAutoModules );
for ( Map.Entry entry : automodules.entrySet() )
{
String moduleName = entry.getValue();
if ( moduleName != null )
{
JavaModuleDescriptor moduleDescriptor = JavaModuleDescriptor.newAutomaticModule( moduleName ).build();
moduleNameSources.put( moduleDescriptor.name(), ModuleNameSource.FILENAME );
availableNamedModules.put( moduleDescriptor.name(), moduleDescriptor );
pathElements.put( entry.getKey(), moduleDescriptor );
}
}
}
Set requiredNamedModules = new HashSet<>();
if ( mainModuleDescriptor != null )
{
requiredNamedModules.add( mainModuleDescriptor.name() );
selectRequires( mainModuleDescriptor,
Collections.unmodifiableMap( availableNamedModules ),
Collections.unmodifiableMap( availableProviders ),
requiredNamedModules,
true );
}
for ( String additionalModule : request.getAdditionalModules() )
{
selectModule( additionalModule,
Collections.unmodifiableMap( availableNamedModules ),
Collections.unmodifiableMap( availableProviders ),
requiredNamedModules );
}
// in case of identical module names, first one wins
Set collectedModules = new HashSet<>( requiredNamedModules.size() );
for ( Entry entry : pathElements.entrySet() )
{
if ( entry.getValue() != null && requiredNamedModules.contains( entry.getValue().name() ) )
{
if ( collectedModules.add( entry.getValue().name() ) )
{
result.getModulepathElements().put( entry.getKey(),
moduleNameSources.get( entry.getValue().name() ) );
}
}
else
{
result.getClasspathElements().add( entry.getKey() );
}
}
return result;
}
private JavaModuleDescriptor getMainModuleDescriptor( final ResolvePathsRequest request )
throws IOException
{
JavaModuleDescriptor mainModuleDescriptor;
Path descriptorPath = request.getMainModuleDescriptor();
if ( descriptorPath != null )
{
if ( descriptorPath.endsWith( "module-info.java" ) )
{
mainModuleDescriptor = sourceParser.fromSourcePath( descriptorPath );
}
else if ( descriptorPath.endsWith( "module-info.class" ) )
{
mainModuleDescriptor = binaryParser.getModuleDescriptor( descriptorPath.getParent() );
}
else
{
throw new IOException( "Invalid path to module descriptor: " + descriptorPath );
}
}
else
{
mainModuleDescriptor = request.getModuleDescriptor();
}
return mainModuleDescriptor;
}
private ResolvePathResult resolvePath( Path path, ModuleNameExtractor fileModulenameExtractor ) throws IOException
{
ResolvePathResult result = new ResolvePathResult();
JavaModuleDescriptor moduleDescriptor = null;
// either jar or outputDirectory
if ( Files.isRegularFile( path ) && !path.getFileName().toString().endsWith( ".jar" ) )
{
throw new IllegalArgumentException( "'" + path.toString() + "' not allowed on the path, only outputDirectories and jars are accepted" );
}
if ( Files.isRegularFile( path ) || Files.exists( path.resolve( "module-info.class" ) ) )
{
moduleDescriptor = binaryParser.getModuleDescriptor( path );
}
if ( moduleDescriptor != null )
{
result.setModuleNameSource( ModuleNameSource.MODULEDESCRIPTOR );
}
else
{
String moduleName = manifestModuleNameExtractor.extract( path );
if ( moduleName != null )
{
result.setModuleNameSource( ModuleNameSource.MANIFEST );
}
else
{
moduleName = fileModulenameExtractor.extract( path );
if ( moduleName != null )
{
result.setModuleNameSource( ModuleNameSource.FILENAME );
}
}
if ( moduleName != null )
{
moduleDescriptor = JavaModuleDescriptor.newAutomaticModule( moduleName ).build();
}
}
result.setModuleDescriptor( moduleDescriptor );
return result;
}
private void selectRequires( JavaModuleDescriptor module,
Map availableModules,
Map> availableProviders,
Set namedModules,
boolean includeStatic )
{
for ( JavaModuleDescriptor.JavaRequires requires : module.requires() )
{
if ( includeStatic || !requires.modifiers().contains( JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC ) )
{
selectModule( requires.name(), availableModules, availableProviders, namedModules );
}
}
for ( String uses : module.uses() )
{
if ( availableProviders.containsKey( uses ) )
{
for ( String providerModule : availableProviders.get( uses ) )
{
JavaModuleDescriptor requiredModule = availableModules.get( providerModule );
if ( requiredModule != null && namedModules.add( providerModule ) )
{
selectRequires( requiredModule, availableModules, availableProviders, namedModules, false );
}
}
}
}
}
private void selectModule( String module, Map availableModules, Map> availableProviders,
Set namedModules )
{
JavaModuleDescriptor requiredModule = availableModules.get( module );
if ( requiredModule != null && namedModules.add( module ) )
{
selectRequires( requiredModule, availableModules, availableProviders, namedModules, false );
}
}
private void collectProviders( JavaModuleDescriptor moduleDescriptor, Map> availableProviders )
{
for ( JavaProvides provides : moduleDescriptor.provides() )
{
// module-info.class uses FQN, i.e. $-separator for subclasses
final String serviceClassName = provides.service().replace( '$', '.' );
Set providingModules = availableProviders.get( serviceClassName );
if ( providingModules == null )
{
providingModules = new HashSet<>();
availableProviders.put( serviceClassName, providingModules );
}
providingModules.add( moduleDescriptor.name() );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy