Skip to content
Commits on Source (5)
......@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-languages</artifactId>
<version>0.9.5</version>
<version>0.9.10</version>
</parent>
<artifactId>plexus-java</artifactId>
......@@ -22,7 +22,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<version>2.21.0</version>
</plugin>
</plugins>
</pluginManagement>
......@@ -31,6 +31,32 @@
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>1.7.1</version>
<!-- WATCH OUT duplicate compiler blocks confuses this plugin -->
<!-- Generated components.xml copied to src/main/resources -->
<configuration>
<extractors>
<extractor>source</extractor>
</extractors>
</configuration>
</plugin>
<!-- Rerun unittests with the multirelease jar, cannot be done with exploded directory of classes -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.21.0</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
......@@ -39,12 +65,12 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.0</version>
<version>6.2</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>2.0-M7</version>
<version>2.0-M8</version>
</dependency>
<dependency>
......@@ -69,7 +95,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<version>2.18.3</version>
<scope>test</scope>
</dependency>
<dependency>
......@@ -80,4 +106,55 @@
</dependency>
</dependencies>
<profiles>
<profile>
<id>jdk9</id>
<activation>
<jdk>[9,)</jdk>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<executions>
<execution>
<id>jdk9</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>9</release>
<compileSourceRoots>
<compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot>
</compileSourceRoots>
<outputDirectory>${project.build.outputDirectory}/META-INF/versions/9</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>
......@@ -23,27 +23,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
/**
* Extract information from module with ASM
*
* @author Robert Scholte
* @since 1.0.0
*/
public class AsmModuleInfoParser
implements ModuleInfoParser
public abstract class AbstractBinaryModuleInfoParser implements ModuleInfoParser
{
private static final Pattern MRJAR_DESCRIPTOR = Pattern.compile( "META-INF/versions/[^/]+/module-info.class" );
......@@ -106,47 +92,5 @@ public class AsmModuleInfoParser
return descriptor;
}
private JavaModuleDescriptor parse( InputStream in )
throws IOException
{
final JavaModuleDescriptorWrapper wrapper = new JavaModuleDescriptorWrapper();
ClassReader reader = new ClassReader( in );
reader.accept( new ClassVisitor( Opcodes.ASM6 )
{
@Override
public ModuleVisitor visitModule( String name, int arg1, String arg2 )
{
wrapper.builder = JavaModuleDescriptor.newModule( name );
return new ModuleVisitor( Opcodes.ASM6 )
{
@Override
public void visitRequire( String module, int access, String version )
{
wrapper.builder.requires( module );
}
@Override
public void visitExport( String pn, int ms, String... targets )
{
if ( targets == null || targets.length == 0 )
{
wrapper.builder.exports( pn.replace( '/', '.' ) );
}
else
{
wrapper.builder.exports( pn.replace( '/', '.' ), new HashSet<>( Arrays.asList( targets ) ) );
}
}
};
}
}, 0 );
return wrapper.builder.build();
}
private static class JavaModuleDescriptorWrapper
{
private JavaModuleDescriptor.Builder builder;
}
abstract JavaModuleDescriptor parse( InputStream in ) throws IOException;
}
package org.codehaus.plexus.languages.java.jpms;
/*
* 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.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
/**
* Extract information from module with ASM
*
*
* @author Robert Scholte
* @since 1.0.0
*/
class BinaryModuleInfoParser extends AbstractBinaryModuleInfoParser
{
@Override
JavaModuleDescriptor parse( InputStream in )
throws IOException
{
final JavaModuleDescriptorWrapper wrapper = new JavaModuleDescriptorWrapper();
ClassReader reader = new ClassReader( in );
reader.accept( new ClassVisitor( Opcodes.ASM6 )
{
@Override
public ModuleVisitor visitModule( String name, int arg1, String arg2 )
{
wrapper.builder = JavaModuleDescriptor.newModule( name );
return new ModuleVisitor( Opcodes.ASM6 )
{
@Override
public void visitRequire( String module, int access, String version )
{
if ( ( access & Opcodes.ACC_STATIC_PHASE ) != 0 )
{
wrapper.builder.requires( Collections.singleton( JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC ),
module );
}
else
{
wrapper.builder.requires( module );
}
}
@Override
public void visitExport( String pn, int ms, String... targets )
{
if ( targets == null || targets.length == 0 )
{
wrapper.builder.exports( pn.replace( '/', '.' ) );
}
else
{
wrapper.builder.exports( pn.replace( '/', '.' ), new HashSet<>( Arrays.asList( targets ) ) );
}
}
};
}
}, 0 );
return wrapper.builder.build();
}
private static class JavaModuleDescriptorWrapper
{
private JavaModuleDescriptor.Builder builder;
}
}
......@@ -21,6 +21,7 @@ package org.codehaus.plexus.languages.java.jpms;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
/**
......@@ -93,6 +94,26 @@ public class JavaModuleDescriptor
return this;
}
/**
* Adds a dependence on a module with the given (and possibly empty) set of modifiers.
*
* @param modifiers The set of modifiers
* @param name The module name
* @return This builder
*/
public Builder requires( Set<JavaModuleDescriptor.JavaRequires.JavaModifier> modifiers, String name )
{
JavaRequires requires = new JavaRequires( modifiers, name );
jModule.requires.add( requires );
return this;
}
/**
* Adds a dependence on a module with an empty set of modifiers.
*
* @param name The module name
* @return This builder
*/
public Builder requires( String name )
{
JavaRequires requires = new JavaRequires( name );
......@@ -100,6 +121,12 @@ public class JavaModuleDescriptor
return this;
}
/**
* Adds an exported package. The package is exported to all modules.
*
* @param source The package name
* @return This builder
*/
public Builder exports( String source )
{
JavaExports exports = new JavaExports( source );
......@@ -107,6 +134,13 @@ public class JavaModuleDescriptor
return this;
}
/**
* Adds an exported package. The package is exported to a set of target modules.
*
* @param source The package name
* @param targets The set of target modules names
* @return This builder
*/
public Builder exports( String source, Set<String> targets )
{
JavaExports exports = new JavaExports( source, targets );
......@@ -114,6 +148,11 @@ public class JavaModuleDescriptor
return this;
}
/**
* Builds and returns a ModuleDescriptor from its components.
*
* @return The module descriptor
*/
public JavaModuleDescriptor build()
{
return jModule;
......@@ -128,17 +167,76 @@ public class JavaModuleDescriptor
*/
public static class JavaRequires
{
private final Set<JavaModifier> modifiers;
private final String name;
private JavaRequires( Set<JavaModifier> modifiers, String name )
{
this.modifiers = modifiers;
this.name = name;
}
private JavaRequires( String name )
{
this.modifiers = Collections.emptySet();
this.name = name;
}
public Set<JavaModifier> modifiers()
{
return modifiers;
}
public String name()
{
return name;
}
/**
* Represents Module.Requires.Modifier
*
* @author Robert Scholte
* @since 1.0.0
*/
public static enum JavaModifier
{
STATIC
}
@Override
public int hashCode()
{
return Objects.hash( modifiers, name );
}
@Override
public boolean equals( Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null )
{
return false;
}
if ( getClass() != obj.getClass() )
{
return false;
}
JavaRequires other = (JavaRequires) obj;
if ( !Objects.equals( modifiers, other.modifiers ) )
{
return false;
}
if ( !Objects.equals( name, other.name ) )
{
return false;
}
return true;
}
}
/**
......@@ -174,5 +272,41 @@ public class JavaModuleDescriptor
{
return targets;
}
@Override
public int hashCode()
{
return Objects.hash( source, targets );
}
@Override
public boolean equals( Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null )
{
return false;
}
if ( getClass() != obj.getClass() )
{
return false;
}
JavaExports other = (JavaExports) obj;
if ( !Objects.equals( source, other.source ) )
{
return false;
}
if ( !Objects.equals( targets, other.targets ) )
{
return false;
}
return true;
}
}
}
......@@ -45,22 +45,30 @@ import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult.ModuleNameSour
@Component( role = LocationManager.class )
public class LocationManager
{
private ModuleInfoParser asmParser;
private ModuleInfoParser binaryParser;
private QDoxModuleInfoParser qdoxParser;
private SourceModuleInfoParser sourceParser;
public LocationManager()
{
this.asmParser = new AsmModuleInfoParser();
this.qdoxParser = new QDoxModuleInfoParser();
this.binaryParser = new BinaryModuleInfoParser();
this.sourceParser = new SourceModuleInfoParser();
}
LocationManager( ModuleInfoParser asmParser, QDoxModuleInfoParser qdoxParser )
LocationManager( ModuleInfoParser binaryParser, SourceModuleInfoParser sourceParser )
{
this.asmParser = asmParser;
this.qdoxParser = qdoxParser;
this.binaryParser = binaryParser;
this.sourceParser = sourceParser;
}
/**
* 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 <T> ResolvePathsResult<T> resolvePaths( ResolvePathsRequest<T> request )
throws IOException
{
......@@ -76,11 +84,11 @@ public class LocationManager
{
if ( descriptorPath.endsWith( "module-info.java" ) )
{
mainModuleDescriptor = qdoxParser.fromSourcePath( descriptorPath );
mainModuleDescriptor = sourceParser.fromSourcePath( descriptorPath );
}
else if ( descriptorPath.endsWith( "module-info.class" ) )
{
mainModuleDescriptor = asmParser.getModuleDescriptor( descriptorPath.getParent() );
mainModuleDescriptor = binaryParser.getModuleDescriptor( descriptorPath.getParent() );
}
else
{
......@@ -114,7 +122,15 @@ public class LocationManager
// either jar or outputDirectory
if ( Files.isRegularFile( path ) || Files.exists( path.resolve( "module-info.class" ) ) )
{
moduleDescriptor = asmParser.getModuleDescriptor( path );
try
{
moduleDescriptor = binaryParser.getModuleDescriptor( path );
}
catch( IOException e )
{
result.getPathExceptions().put( t, e );
continue;
}
}
if ( moduleDescriptor != null )
......@@ -137,7 +153,15 @@ public class LocationManager
}
else
{
moduleName = MainClassModuleNameExtractor.getModuleName( path );
try
{
moduleName = MainClassModuleNameExtractor.getModuleName( path );
}
catch ( Exception e )
{
result.getPathExceptions().put( t, e );
continue;
}
if ( moduleName != null )
{
......@@ -192,13 +216,22 @@ public class LocationManager
requiredNamedModules.add( mainModuleDescriptor.name() );
requiredNamedModules.addAll( request.getAdditionalModules() );
select( mainModuleDescriptor, Collections.unmodifiableMap( availableNamedModules ), requiredNamedModules );
// in case of identical module names, first one wins
Set<String> collectedModules = new HashSet<>( requiredNamedModules.size() );
for ( Entry<T, JavaModuleDescriptor> entry : pathElements.entrySet() )
{
if ( entry.getValue() != null && requiredNamedModules.contains( entry.getValue().name() ) )
{
result.getModulepathElements().put( entry.getKey(), moduleNameSources.get( entry.getValue().name() ) );
if ( collectedModules.add( entry.getValue().name() ) )
{
result.getModulepathElements().put( entry.getKey(),
moduleNameSources.get( entry.getValue().name() ) );
}
}
else
{
......
......@@ -22,6 +22,7 @@ package org.codehaus.plexus.languages.java.jpms;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
......@@ -78,7 +79,11 @@ public class MainClassModuleNameExtractor
for ( Path p : files.values() )
{
argsWriter.append( p.toAbsolutePath().toString() );
// make sure the path is surrounded with quotes in case there is space
argsWriter.append( '"' );
// make sure to escape Windows paths
argsWriter.append( p.toAbsolutePath().toString().replace( "\\", "\\\\" ) );
argsWriter.append( '"' );
argsWriter.newLine();
}
}
......@@ -135,10 +140,17 @@ public class MainClassModuleNameExtractor
for ( String path : args )
{
String moduleName = getModuleName( Paths.get( path ) );
if ( moduleName != null )
try
{
properties.setProperty( path, moduleName );
String moduleName = getModuleName( Paths.get( path ) );
if ( moduleName != null )
{
properties.setProperty( path, moduleName );
}
}
catch ( Exception e )
{
System.err.append( e.getMessage() );
}
}
......@@ -152,7 +164,7 @@ public class MainClassModuleNameExtractor
}
}
public static String getModuleName( Path modulePath )
public static String getModuleName( Path modulePath ) throws Exception
{
String name = null;
try
......@@ -181,6 +193,13 @@ public class MainClassModuleNameExtractor
Method nameMethod = moduleDescriptorInstance.getClass().getMethod( "name" );
name = (String) nameMethod.invoke( moduleDescriptorInstance );
}
catch ( InvocationTargetException e )
{
if ( e.getCause() instanceof Exception )
{
throw (Exception) e.getCause();
}
}
catch ( ReflectiveOperationException e )
{
// noop
......
......@@ -33,7 +33,7 @@ import java.util.jar.Manifest;
* @author Robert Scholte
* @since 1.0.0
*/
public class ManifestModuleNameExtractor implements ModuleNameExtractor
class ManifestModuleNameExtractor implements ModuleNameExtractor
{
@Override
public String extract( Path file )
......
......@@ -28,16 +28,14 @@ import java.nio.file.Path;
* @author Robert Scholte
* @since 1.0.0
*/
public interface ModuleInfoParser
interface ModuleInfoParser
{
/**
* Extracts the name from the module-info file
*
* @param modulePath
* @return
* @throws IOException
* @param modulePath the path to the {@code module-info.class}
* @return the module descriptor
* @throws IOException when the file could not be parsed
*/
JavaModuleDescriptor getModuleDescriptor( Path modulePath )
throws IOException;
......
......@@ -22,7 +22,7 @@ package org.codehaus.plexus.languages.java.jpms;
import java.io.IOException;
import java.nio.file.Path;
public interface ModuleNameExtractor
interface ModuleNameExtractor
{
String extract( Path path ) throws IOException;
}
......@@ -23,6 +23,7 @@ import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
/**
* Contains all information required to analyze the project
......@@ -37,12 +38,23 @@ public abstract class ResolvePathsRequest<T>
private Path mainModuleDescriptor;
private Collection<T> pathElements;
private Collection<String> additionalModules;
private ResolvePathsRequest()
{
}
/**
* @deprecated use {@link #ofFiles(Collection)} instead
*/
@Deprecated
public static ResolvePathsRequest<File> withFiles( Collection<File> files )
{
return ofFiles( files );
}
public static ResolvePathsRequest<File> ofFiles( Collection<File> files )
{
ResolvePathsRequest<File> request = new ResolvePathsRequest<File>()
{
......@@ -52,12 +64,21 @@ public abstract class ResolvePathsRequest<T>
return t.toPath();
}
};
request.pathElements = files;
return request;
}
/**
* @deprecated use {@link #ofPaths(Collection)} instead
*/
@Deprecated
public static ResolvePathsRequest<Path> withPaths( Collection<Path> paths )
{
return ofPaths( paths );
}
public static ResolvePathsRequest<Path> ofPaths( Collection<Path> paths )
{
ResolvePathsRequest<Path> request = new ResolvePathsRequest<Path>() {
@Override
......@@ -69,8 +90,17 @@ public abstract class ResolvePathsRequest<T>
request.pathElements = paths;
return request;
}
/**
* @deprecated use {@link #ofStrings(Collection)} instead
*/
@Deprecated
public static ResolvePathsRequest<String> withStrings( Collection<String> strings )
{
return ofStrings( strings );
}
public static ResolvePathsRequest<String> ofStrings( Collection<String> strings )
{
ResolvePathsRequest<String> request = new ResolvePathsRequest<String>() {
@Override
......@@ -82,13 +112,13 @@ public abstract class ResolvePathsRequest<T>
request.pathElements = strings;
return request;
}
protected abstract Path toPath( T t );
final ResolvePathsResult<T> createResult() {
return new ResolvePathsResult<>();
}
public Path getMainModuleDescriptor()
{
return mainModuleDescriptor;
......@@ -115,6 +145,7 @@ public abstract class ResolvePathsRequest<T>
* In case the JRE is Java 8 or before, this jdkHome is used to extract the module name.
*
* @param jdkHome
* @return this request
*/
public ResolvePathsRequest<T> setJdkHome( T jdkHome )
{
......@@ -127,4 +158,24 @@ public abstract class ResolvePathsRequest<T>
return jdkHome;
}
/**
* The module names that are usually passed with {@code --add-modules}
*
* @param additionalModules
* @return this request
*/
public ResolvePathsRequest<T> setAdditionalModules( Collection<String> additionalModules )
{
this.additionalModules = additionalModules;
return this;
}
public Collection<String> getAdditionalModules()
{
if ( additionalModules == null )
{
additionalModules = Collections.emptyList();
}
return additionalModules;
}
}
......@@ -22,6 +22,7 @@ package org.codehaus.plexus.languages.java.jpms;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
......@@ -47,15 +48,23 @@ public class ResolvePathsResult<T>
*/
private Map<T, JavaModuleDescriptor> pathElements;
private Map<T, ModuleNameSource> modulepathElements = new HashMap<>();
private Map<T, ModuleNameSource> modulepathElements = new LinkedHashMap<>();
private Collection<T> classpathElements = new ArrayList<>();
private Map<T, Exception> pathExceptions = new HashMap<>();
void setMainModuleDescriptor( JavaModuleDescriptor mainModuleDescriptor )
{
this.mainModuleDescriptor = mainModuleDescriptor;
}
/**
* The resolved main module descriptor
*
* @return the resolved descriptor
* @see ResolvePathsRequest#setMainModuleDescriptor(Object)
*/
public JavaModuleDescriptor getMainModuleDescriptor()
{
return mainModuleDescriptor;
......@@ -79,6 +88,12 @@ public class ResolvePathsResult<T>
this.classpathElements = classpathElements;
}
/**
* All T that belong to the classpath based on the module descriptor
*
* @return the classpath elements, never {@code null}
* @see #getPathElements()
*/
public Collection<T> getClasspathElements()
{
return classpathElements;
......@@ -89,8 +104,30 @@ public class ResolvePathsResult<T>
this.modulepathElements = modulepathElements;
}
/**
* All T that belong to the modulepath, based on the module descriptor.
* For every T the source for the module name is added.
*
* @return all modulepath elements, never {@code null}
* @see #getPathElements()
*/
public Map<T, ModuleNameSource> getModulepathElements()
{
return modulepathElements;
}
void setPathExceptions( Map<T, Exception> pathExceptions )
{
this.pathExceptions = pathExceptions;
}
/**
* Map containing exceptions for every T which modulename resolution failed
*
* @return the exceptions for every T, never {@code null}
*/
public Map<T, Exception> getPathExceptions()
{
return pathExceptions;
}
}
......@@ -22,6 +22,7 @@ package org.codehaus.plexus.languages.java.jpms;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
......@@ -35,7 +36,7 @@ import com.thoughtworks.qdox.model.JavaModuleDescriptor;
* @author Robert Scholte
* @since 1.0.0
*/
public class QDoxModuleInfoParser
class SourceModuleInfoParser
{
public org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor fromSourcePath( Path modulePath )
......@@ -52,7 +53,15 @@ public class QDoxModuleInfoParser
for ( JavaModuleDescriptor.JavaRequires requires : descriptor.getRequires() )
{
builder.requires( requires.getModule().getName() );
if ( requires.isStatic() )
{
builder.requires( Collections.singleton( org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC ),
requires.getModule().getName() );
}
else
{
builder.requires( requires.getModule().getName() );
}
}
for ( JavaModuleDescriptor.JavaExports exports : descriptor.getExports() )
......
package org.codehaus.plexus.languages.java.version;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
......@@ -26,11 +27,22 @@ import java.util.regex.Pattern;
* @author Robert Scholte
* @since 1.0.0
*
* @see <a href="http://www.oracle.com/technetwork/java/javase/namechange-140185.html">http://www.oracle.com/technetwork/java/javase/namechange-140185.html</a>
* @see <a href="http://openjdk.java.net/jeps/223">http://openjdk.java.net/jeps/223</a>
* @see <a href="http://www.oracle.com/technetwork/java/javase/namechange-140185.html">Java SE Naming and Versions</a>
* @see <a href="http://openjdk.java.net/jeps/223">JEP 223: New Version-String Scheme</a>
* @see <a href="http://openjdk.java.net/jeps/322">JEP 322: Time-Based Release Versioning</a>
*/
public class JavaVersion implements Comparable<JavaVersion>
{
/**
* Represents the System property {@code java.specification.version}
*/
public static final JavaVersion JAVA_SPECIFICATION_VERSION = parse( System.getProperty( "java.specification.version" ) );
/**
* Represents the System property {@code java.version}
*/
public static final JavaVersion JAVA_VERSION = parse( System.getProperty( "java.version" ) );
private static final Pattern startingDigits = Pattern.compile( "(\\d+)(.*)" );
private String rawVersion;
......@@ -41,7 +53,8 @@ public class JavaVersion implements Comparable<JavaVersion>
}
/**
* Parser only the version-scheme.
* Lazy parse the version-scheme.
* Actual parsing is done when calling {@link #compareTo(JavaVersion)}
*
* @param s the version string
* @return the version wrapped in a JavadocVersion
......@@ -127,9 +140,83 @@ public class JavaVersion implements Comparable<JavaVersion>
}
}
/**
* Verify if this version is before some other version
*
* @param other the version to compare with
* @return {@code true} is this is less than {@code other}, otherwise {@code false}
*/
public boolean isBefore( JavaVersion other )
{
return this.compareTo( other ) < 0;
}
/**
* Verify if this version is before some other version
*
* @param other the version to compare with
* @return {@code true} is this is less than {@code other}, otherwise {@code false}
*/
public boolean isBefore( String other )
{
return this.compareTo( parse( other ) ) < 0;
}
/**
* Verify if this version is at least some other version
*
* @param other the version to compare with
* @return {@code true} is this is greater than or equal to {@code other}, otherwise {@code false}
*/
public boolean isAtLeast( JavaVersion other )
{
return this.compareTo( other ) >= 0;
}
/**
* Verify if this version is at least some other version
*
* @param other the version to compare with
* @return {@code true} is this is greater than or equal to {@code other}, otherwise {@code false}
*/
public boolean isAtLeast( String other )
{
return this.compareTo( parse( other ) ) >= 0;
}
@Override
public String toString()
{
return rawVersion;
}
@Override
public int hashCode()
{
return Objects.hashCode( rawVersion );
}
@Override
public boolean equals( Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null )
{
return false;
}
if ( getClass() != obj.getClass() )
{
return false;
}
JavaVersion other = (JavaVersion) obj;
if ( !Objects.equals( rawVersion, other.rawVersion ) )
{
return false;
}
return true;
}
}
package org.codehaus.plexus.languages.java.jpms;
/*
* 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.io.InputStream;
import java.lang.module.ModuleDescriptor;
import java.util.Collections;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.Builder;
class BinaryModuleInfoParser extends AbstractBinaryModuleInfoParser
{
@Override
JavaModuleDescriptor parse( InputStream in ) throws IOException
{
ModuleDescriptor descriptor = ModuleDescriptor.read( in );
Builder builder = JavaModuleDescriptor.newModule( descriptor.name() );
for ( ModuleDescriptor.Requires requires : descriptor.requires() )
{
if ( requires.modifiers().contains( ModuleDescriptor.Requires.Modifier.STATIC ) )
{
builder.requires( Collections.singleton( org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC ),
requires.name() );
}
else
{
builder.requires( requires.name() );
}
}
for ( ModuleDescriptor.Exports exports : descriptor.exports() )
{
if ( exports.targets().isEmpty() )
{
builder.exports( exports.source() );
}
else
{
builder.exports( exports.source(), exports.targets() );
}
}
return builder.build();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<component-set>
<components>
<component>
<role>org.codehaus.plexus.languages.java.jpms.LocationManager</role>
<role-hint>default</role-hint>
<implementation>org.codehaus.plexus.languages.java.jpms.LocationManager</implementation>
<description></description>
<isolated-realm>false</isolated-realm>
</component>
</components>
</component-set>
The plexus-java library is created to have a solution for common activities, so this business logic doesn't have to be maintained at multiple places. The first provided feature was the `LocationManager` to analyze module desciptors and to decide which jars should end up on the modulepath and which on the classpath. The name was based on the [javax.tools.JavaFileManager.Location]. (https://docs.oracle.com/javase/10/docs/api/javax/tools/JavaFileManager.Location.html)
The library requires Java 7 to run, but contains optimized code for Java 9. By requiring Java 7 it was much easier to embed this library in several other projects.
This jar is a multi release jar (aka MRJAR), because it contains 2 implementations for the `BinaryModuleInfoParser`. If the Java runtime is 9 or above, the `java.lang.module.ModuleDescriptor` is used to read the `module-info.class`. If the runtime is Java 7 or Java 8, then ASM is used to read the module descriptor.
When extracting the the automatic module name based the of the file, it is a little bit more complex. The result must be precise, so the only way to solve this is by calling Java 9 code, either from the runtime or by calling Java 9 explicitly when provided via `ResolvePathsRequest.setJdkHome`.
## Request
The `LocationManager.resolvePaths()` only has one argument, `ResolvePathsRequest`. If there is more data required, the request will be extended so the method signature of `resolvePaths` will stay the same.
There are 3 ways to create a `ResolvePathsRequest`:
- `ResolvePathsRequest.ofFiles(Collection<File>)`
- `ResolvePathsRequest.ofPaths(Collection<Path>)`
- `ResolvePathsRequest.ofStrings(Collection<String>)`
As argument you pass all the archives and/or outputDirectories specified to build the project.
Additional methods are:
- `setAdditionalModules`, in case the consumer wants to use `--add-modules`
- `setJdkHome`, should point to Java 9 or above in case the runtime of this library is Java 7 or 8
- `setMainModuleDescriptor`, which can either be a `module-info.java` or `module-info.class`
## Phase 1: Collect
If there's a `mainModuleDescriptor`, extract a `JavaModuleDescriptor` of it. This might cause a `IOException` to be thrown.
All pathElements of `ResolvePathsRequest.ofT` are transformed to Path instances. For every element the name will be resolved in the following order:
1. Module descriptor: verify if the jar or the directory contains `module-info.class`. If so, the its descriptor is transformed to a `JavaModuleDescriptor`, where its ModuleNameSource is marked as `ModuleNameSource.MODULEDESCRIPTOR`
2. Manifest: verify if the jar or directory has a `META-INF/MANIFEST.MF` and if it contains the `Automatic-Module-Name` attribute. If so, an automatic `JavaModuleDescriptor` is created, where its ModuleNameSource is marked as `ModuleNameSource.MANIFEST`.
3. Filename: try to extract the module name based on the filename. If the filename could be transformed to a module name (which is not always the case), an automatic `JavaModuleDescriptor` is created, where its ModuleNameSource is marked as `ModuleNameSource.FILENAME`.
When there's an `IOException` with one of the pathElements, the exception is stored in the `ResolvePathsResult.pathExceptions` so the consumer can handle them separately.
The result are a couple of Maps:
* module name to `ModuleNameSource`
* module name to `JavaModuleDescriptor`
## Phase 2: Resolve
If there's a `mainModuleDescriptor`, collect all its direct and indirect requirements.
This contains recursive code and ensures that required modules are only evaluated once.
All these pathElements must be placed on the modulepath, all other pathElements will be marked for the classPath.
## Result
All results will be stored in a `ResolvePathsResult`.
- `getClasspathElements()`, ordered collection of all pathElements that don't belong to the modulepath
- `getMainModuleDescriptor()`, an `JavaModuleDescriptor` instance based on the provided mainModuleDescriptor file
- `getModulepathElements()`, ordered map of the pathElements with their source
- `getPathElements()`, ordered map of the pathElements with their module descriptor
- `getPathExceptions()`, map of pathElements containing only the elements that faced an exception.
......@@ -3,16 +3,38 @@
In order to use this class you must setup a `ResolvePathsRequest`, which requires a list of all the jars and output directories and the main module descriptor.
You start by using `ResolvePathsRequest.withXXX`, where XXX is either Files, Paths or Strings. This way the Result will contain the same type of objects.
You start by using `ResolvePathsRequest.ofXXX`, where XXX is either Files, Paths or Strings. This way the Result will contain the same type of objects.
For the module descriptor you will either use the QDoxModuleInfoParser (in case of `module-info.java`) or AsmModuleInfoParser(in case of `module-info.class`).
The `ResolvePathRequest` also contains:
* mainModuleDescriptor: the path or file of the main module descriptor, can either be `mdouel-info.java` or `module-info.class`
* additionalModules: the modules that will be addedusing `-add-modules`
* jdkHome: in case you need to use a different JDK to extract the name from the modules. Can be interesting if the runtime is still Java 7.
The `ResolvePathsResult` contains:
* mainModuleDescriptor: the module descriptor of the passed descriptor file.
* pathElements: as a map in the same order as provided by the request. Every entry has a matching moduledescriptor when available.
* classpathElements: all the pathElements which should end up on the classpath
* classpathElements: all the pathElements which should end up on the classpath.
* modulepathElements: all the pathElements which should end up on the modulepath. Per entry you get the source of the modulename which is either the moduledescriptor, the manifestfile of the filename. This information can be used to warn users in case they use automatic modules, which module names are not reliable yet.
* pathExceptions: pathElements with their exception while trying to resolve it. Only pathElements with an exception are listed.
## JavaVersion
This is a String based, lazy-parsing implementation of a Java Version which can be used to compare versions. It's goal is to support to support the following patterns:
* [Java SE Naming and Versions](http://www.oracle.com/technetwork/java/javase/namechange-140185.html)
* [JEP 223: New Version-String Scheme](http://openjdk.java.net/jeps/223)
* [JEP 322: Time-Based Release Versioning](http://openjdk.java.net/jeps/322)
Additional features:
* `JavaVersion.JAVA_SPECIFICATION_VERSION` represents `System.getProperty( "java.specification.version" )`
* `JavaVersion.JAVA_VERSION` represents `System.getProperty( "java.version" )`
\ No newline at end of file
......@@ -60,4 +60,11 @@ public abstract class AbstractFilenameModuleNameExtractorTest
assertEquals( null, name );
}
@Test
public void testJarWithSpacesInPath() throws Exception
{
String name = getExtractor().extract( Paths.get( "src/test/resources/jar with spaces in path/plexus-java-1.0.0-SNAPSHOT.jar" ) );
assertEquals( "org.codehaus.plexus.languages.java", name );
}
}
......@@ -23,18 +23,19 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Collections;
import java.util.Set;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaExports;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaRequires;
import org.junit.Test;
public class AsmModuleInfoParserTest
public class BinaryModuleInfoParserTest
{
private ModuleInfoParser parser = new AsmModuleInfoParser();
private BinaryModuleInfoParser parser = new BinaryModuleInfoParser();
@Test
public void testJarDescriptor() throws Exception
......@@ -48,10 +49,12 @@ public class AsmModuleInfoParserTest
assertEquals( 1, descriptor.requires().size() );
assertEquals( "java.base", descriptor.requires().iterator().next().name() );
// Actually the source returns "org/objectweb/asm" instead "org.objectweb.asm" ?
// Need to dive into this later.
// assertEquals( 2, descriptor.exports().size() );
// assertEquals( "org.objectweb.asm", descriptor.exports().iterator().next().source() );
Set<JavaExports> expectedExports = JavaModuleDescriptor.newAutomaticModule( "_" )
.exports( "org.objectweb.asm" )
.exports( "org.objectweb.asm.signature" )
.build()
.exports();
assertEquals( expectedExports, descriptor.exports() );
}
@Test
......@@ -82,22 +85,26 @@ public class AsmModuleInfoParserTest
}
@Test
public void testOutputDirectoryDescriptor() throws Exception
public void testOutputDirectoryDescriptor()
throws Exception
{
JavaModuleDescriptor descriptor = parser.getModuleDescriptor( Paths.get( "src/test/resources/dir.descriptor/out" ) );
assertNotNull( descriptor);
assertEquals( "org.objectweb.asm.all", descriptor.name() );
JavaModuleDescriptor descriptor =
parser.getModuleDescriptor( Paths.get( "src/test/resources/dir.descriptor/out" ) );
assertNotNull( descriptor );
assertEquals( "org.codehaus.plexus.languages.java.demo", descriptor.name() );
assertEquals( false, descriptor.isAutomatic() );
assertEquals( 2, descriptor.requires().size() );
Set<String> actualNames = new HashSet<>( 2 );
for ( JavaRequires require : descriptor.requires() )
{
actualNames.add( require.name() );
}
Set<String> expectedNames = new HashSet<>( Arrays.asList( "java.base", "java.xml" ) );
assertEquals( expectedNames, actualNames );
assertEquals( 3, descriptor.requires().size() );
Set<JavaRequires> expectedRequires = JavaModuleDescriptor.newAutomaticModule( "_" )
.requires( "java.base" )
.requires( "java.xml" )
.requires( Collections.singleton( JavaRequires.JavaModifier.STATIC ), "com.google.common" )
.build()
.requires();
assertEquals( expectedRequires, descriptor.requires() );
}
@Test( expected = NoSuchFileException.class )
......@@ -121,5 +128,11 @@ public class AsmModuleInfoParserTest
assertEquals ( 1, descriptor.exports().size() );
assertEquals ( "com.corporate.project", descriptor.exports().iterator().next().source() );
}
@Test( expected = IOException.class )
public void testInvalidFile() throws Exception
{
parser.getModuleDescriptor( Paths.get( "src/test/resources/nonjar/pom.xml" ) );
}
}
......@@ -20,14 +20,19 @@ package org.codehaus.plexus.languages.java.jpms;
*/
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult.ModuleNameSource;
......@@ -35,16 +40,15 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith( MockitoJUnitRunner.class )
@RunWith( org.mockito.junit.MockitoJUnitRunner.class )
public class LocationManagerTest
{
@Mock
private ModuleInfoParser asmParser;
private BinaryModuleInfoParser asmParser;
@Mock
private QDoxModuleInfoParser qdoxParser;
private SourceModuleInfoParser qdoxParser;
private LocationManager locationManager;
......@@ -59,11 +63,12 @@ public class LocationManagerTest
@Test
public void testNoPaths() throws Exception
{
ResolvePathsResult<File> result = locationManager.resolvePaths( ResolvePathsRequest.withFiles( Collections.<File>emptyList() ) );
ResolvePathsResult<File> result = locationManager.resolvePaths( ResolvePathsRequest.ofFiles( Collections.<File>emptyList() ) );
assertThat( result.getMainModuleDescriptor(), nullValue( JavaModuleDescriptor.class) );
assertThat( result.getPathElements().size(), is( 0 ) );
assertThat( result.getModulepathElements().size(), is( 0 ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
......@@ -71,7 +76,7 @@ public class LocationManagerTest
{
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "java.base" ).requires( "jdk.net" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<File> request = ResolvePathsRequest.withFiles( Collections.<File>emptyList() ).setMainModuleDescriptor( mockModuleInfoJava.toFile() );
ResolvePathsRequest<File> request = ResolvePathsRequest.ofFiles( Collections.<File>emptyList() ).setMainModuleDescriptor( mockModuleInfoJava.toFile() );
ResolvePathsResult<File> result = locationManager.resolvePaths( request );
......@@ -79,6 +84,7 @@ public class LocationManagerTest
assertThat( result.getPathElements().size(), is( 0 ) );
assertThat( result.getModulepathElements().size(), is( 0 ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
......@@ -87,9 +93,7 @@ public class LocationManagerTest
Path abc = Paths.get( "src/test/resources/empty/out" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "a.b.c" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request = ResolvePathsRequest.withPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
when( asmParser.getModuleDescriptor( abc ) ).thenReturn( JavaModuleDescriptor.newModule( "def" ).build() );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsResult<Path> result = locationManager.resolvePaths( request );
......@@ -97,6 +101,7 @@ public class LocationManagerTest
assertThat( result.getPathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().size(), is( 0 ) );
assertThat( result.getClasspathElements().size(), is( 1 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
......@@ -105,7 +110,7 @@ public class LocationManagerTest
Path abc = Paths.get( "src/test/resources/manifest.without/out" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "any" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request = ResolvePathsRequest.withPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
// when( reflectParser.getModuleDescriptor( abc ) ).thenReturn( JavaModuleDescriptor.newAutomaticModule( "auto.by.manifest" ).build() );
......@@ -115,6 +120,7 @@ public class LocationManagerTest
assertThat( result.getPathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().size(), is( 0 ) );
assertThat( result.getClasspathElements().size(), is( 1 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
......@@ -123,7 +129,7 @@ public class LocationManagerTest
Path abc = Paths.get( "src/test/resources/dir.manifest.with/out" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "auto.by.manifest" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request = ResolvePathsRequest.withPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
// when( reflectParser.getModuleDescriptor( abc ) ).thenReturn( JavaModuleDescriptor.newAutomaticModule( "auto.by.manifest" ).build() );
......@@ -134,6 +140,7 @@ public class LocationManagerTest
assertThat( result.getModulepathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().get( abc), is( ModuleNameSource.MANIFEST ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
......@@ -142,7 +149,7 @@ public class LocationManagerTest
Path abc = Paths.get( "src/test/resources/dir.descriptor/out" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "dir.descriptor" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request = ResolvePathsRequest.withPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
when( asmParser.getModuleDescriptor( abc ) ).thenReturn( JavaModuleDescriptor.newModule( "dir.descriptor" ).build() );
......@@ -153,6 +160,7 @@ public class LocationManagerTest
assertThat( result.getModulepathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().get( abc), is( ModuleNameSource.MODULEDESCRIPTOR ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
......@@ -161,7 +169,7 @@ public class LocationManagerTest
Path abc = Paths.get( "src/test/resources/jar.descriptor/asm-6.0_BETA.jar" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "org.objectweb.asm" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request = ResolvePathsRequest.withPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Collections.singletonList( abc ) ).setMainModuleDescriptor( mockModuleInfoJava );
when( asmParser.getModuleDescriptor( abc ) ).thenReturn( JavaModuleDescriptor.newModule( "org.objectweb.asm" ).build() );
......@@ -172,6 +180,79 @@ public class LocationManagerTest
assertThat( result.getModulepathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().get( abc), is( ModuleNameSource.MODULEDESCRIPTOR ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
public void testIdenticalModuleNames() throws Exception
{
Path pj1 = Paths.get( "src/test/resources/jar.empty/plexus-java-1.0.0-SNAPSHOT.jar" );
Path pj2 = Paths.get( "src/test/resources/jar.empty.2/plexus-java-2.0.0-SNAPSHOT.jar" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).requires( "plexus.java" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Arrays.asList( pj1, pj2 ) ).setMainModuleDescriptor( mockModuleInfoJava );
when( asmParser.getModuleDescriptor( pj1 ) ).thenReturn( JavaModuleDescriptor.newAutomaticModule( "plexus.java" ).build() );
when( asmParser.getModuleDescriptor( pj2 ) ).thenReturn( JavaModuleDescriptor.newAutomaticModule( "plexus.java" ).build() );
ResolvePathsResult<Path> result = locationManager.resolvePaths( request );
assertThat( result.getMainModuleDescriptor(), is( descriptor) );
assertThat( result.getPathElements().size(), is( 2 ) );
assertThat( result.getModulepathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().containsKey( pj1 ), is( true ) );
assertThat( result.getModulepathElements().containsKey( pj2 ), is( false ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
@Test
public void testClassicJarNameStartsWithNumber() throws Exception
{
assumeThat( "Requires at least Java 9", System.getProperty( "java.version" ), not( startsWith( "1." ) ) );
Path p = Paths.get( "src/test/resources/jar.empty.invalid.name/101-1.0.0-SNAPSHOT.jar" );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Arrays.asList( p ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsResult<Path> result = locationManager.resolvePaths( request );
assertThat( result.getPathExceptions().size(), is( 1 ) );
}
@Test
public void testNonJar() throws Exception
{
Path p = Paths.get( "src/test/resources/nonjar/pom.xml" );
when( asmParser.getModuleDescriptor( p ) ).thenThrow( new IOException() );
ResolvePathsRequest<Path> request = ResolvePathsRequest.ofPaths( Arrays.asList( p ) ).setMainModuleDescriptor( mockModuleInfoJava );
ResolvePathsResult<Path> result = locationManager.resolvePaths( request );
assertThat( result.getPathExceptions().size(), is( 1 ) );
}
@Test
public void testAdditionalModules() throws Exception
{
Path p = Paths.get( "src/test/resources/jar.empty/plexus-java-1.0.0-SNAPSHOT.jar" );
JavaModuleDescriptor descriptor = JavaModuleDescriptor.newModule( "base" ).build();
when( qdoxParser.fromSourcePath( any( Path.class ) ) ).thenReturn( descriptor );
ResolvePathsRequest<Path> request =
ResolvePathsRequest.ofPaths( Arrays.asList( p ) )
.setMainModuleDescriptor( mockModuleInfoJava )
.setAdditionalModules( Collections.singletonList( "plexus.java" ) );
when( asmParser.getModuleDescriptor( p ) ).thenReturn( JavaModuleDescriptor.newAutomaticModule( "plexus.java" ).build() );
ResolvePathsResult<Path> result = locationManager.resolvePaths( request );
assertThat( result.getMainModuleDescriptor(), is( descriptor) );
assertThat( result.getPathElements().size(), is( 1 ) );
assertThat( result.getModulepathElements().size(), is( 1 ) );
assertThat( result.getClasspathElements().size(), is( 0 ) );
assertThat( result.getPathExceptions().size(), is( 0 ) );
}
}