Skip to content

Commit

Permalink
[MJAR-238] Allow setting of module main class
Browse files Browse the repository at this point in the history
Closes #2 from plamentotev:modular-jar-files
  • Loading branch information
plamentotev authored and Enrico Olivelli - Diennea committed Jan 16, 2019
1 parent 13d5a9e commit 78dbb29
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 3 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@

<properties>
<mavenArchiverVersion>3.3.0</mavenArchiverVersion>
<mavenFileManagementVersion>3.0.0</mavenFileManagementVersion>
<mavenVersion>3.0</mavenVersion>
<javaVersion>7</javaVersion>
</properties>
Expand All @@ -96,6 +97,11 @@
<artifactId>maven-artifact</artifactId>
<version>${mavenVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>file-management</artifactId>
<version>${mavenFileManagementVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-archiver</artifactId>
Expand Down
18 changes: 18 additions & 0 deletions src/it/MJAR-238-modular-jar-main-class/invoker.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 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.

invoker.java.version = 9+
57 changes: 57 additions & 0 deletions src/it/MJAR-238-modular-jar-main-class/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>mjar-238-modular-jar-main-class</artifactId>
<name>mjar-238-modular-jar-main-class</name>
<description>Verifies that the modular descriptor main class is set for modular Jar files</description>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>@project.version@</version>
<configuration>
<archive>
<manifest>
<mainClass>myproject.HelloWorld</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>

<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.9</source>
<target>1.9</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

module myproject {
exports myproject;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package myproject;

/*
* 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.
*/

/**
* The classic Hello World App.
*/
public class HelloWorld {

/**
* Main method.
*
* @param args Not used
*/
public static void main( String[] args )
{
System.out.println( "Hi!" );
}
}
75 changes: 75 additions & 0 deletions src/it/MJAR-238-modular-jar-main-class/verify.bsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.*;
import java.lang.module.*;
import java.util.jar.*;

boolean result = true;
JarFile jar;

try
{
File target = new File( basedir, "target" );
if ( !target.exists() || !target.isDirectory() )
{
System.err.println( "target file is missing or not a directory." );
return false;
}

File artifact = new File( target, "mjar-238-modular-jar-main-class-1.0-SNAPSHOT.jar" );
if ( !artifact.exists() || artifact.isDirectory() )
{
System.err.println( "artifact file is missing or a directory." );
return false;
}

jar = new JarFile( artifact );

Attributes manifest = jar.getManifest().getMainAttributes();

if ( !"myproject.HelloWorld".equals( manifest.get( Attributes.Name.MAIN_CLASS ) ) )
{
System.err.println( "Manifest main class attribute not equals myproject.HelloWorld" );
return false;
}

InputStream moduleDescriptorInputStream = jar.getInputStream( jar.getEntry( "module-info.class" ) );
ModuleDescriptor moduleDescriptor = ModuleDescriptor.read( moduleDescriptorInputStream );

if ( !"myproject.HelloWorld".equals( moduleDescriptor.mainClass().orElse( null ) ) )
{
System.err.println( "Module descriptor main class not equals myproject.HelloWorld" );
return false;
}
}
catch( Throwable e )
{
e.printStackTrace();
result = false;
}
finally
{
if ( jar != null )
{
jar.close();
}
}

return result;
44 changes: 41 additions & 3 deletions src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.util.FileSetManager;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;

import java.io.File;
import java.util.Arrays;
import java.util.Map;

/**
* Base class for creating a jar from project classes.
Expand All @@ -47,6 +51,8 @@ public abstract class AbstractJarMojo

private static final String[] DEFAULT_INCLUDES = new String[] { "**/**" };

private static final String MODULE_DESCRIPTOR_FILE_NAME = "module-info.class";

/**
* List of files to include. Specified as fileset patterns which are relative to the input directory whose contents
* is being packaged into the JAR.
Expand Down Expand Up @@ -76,8 +82,8 @@ public abstract class AbstractJarMojo
/**
* The Jar archiver.
*/
@Component( role = Archiver.class, hint = "jar" )
private JarArchiver jarArchiver;
@Component
private Map<String, Archiver> archivers;

/**
* The {@link {MavenProject}.
Expand Down Expand Up @@ -201,9 +207,41 @@ public File createArchive()
{
File jarFile = getJarFile( outputDirectory, finalName, getClassifier() );

FileSetManager fileSetManager = new FileSetManager();
FileSet jarContentFileSet = new FileSet();
jarContentFileSet.setDirectory( getClassesDirectory().getAbsolutePath() );
jarContentFileSet.setIncludes( Arrays.asList( getIncludes() ) );
jarContentFileSet.setExcludes( Arrays.asList( getExcludes() ) );

boolean containsModuleDescriptor = false;
String[] includedFiles = fileSetManager.getIncludedFiles( jarContentFileSet );
for ( String includedFile : includedFiles )
{
// May give false positives if the files is named as module descriptor
// but is not in the root of the archive or in the versioned area
// (and hence not actually a module descriptor).
// That is fine since the modular Jar archiver will gracefully
// handle such case.
// And also such case is unlikely to happen as file ending
// with "module-info.class" is unlikely to be included in Jar file
// unless it is a module descriptor.
if ( includedFile.endsWith( MODULE_DESCRIPTOR_FILE_NAME ) )
{
containsModuleDescriptor = true;
break;
}
}

MavenArchiver archiver = new MavenArchiver();

archiver.setArchiver( jarArchiver );
if ( containsModuleDescriptor )
{
archiver.setArchiver( (JarArchiver) archivers.get( "mjar" ) );
}
else
{
archiver.setArchiver( (JarArchiver) archivers.get( "jar" ) );
}

archiver.setOutputFile( jarFile );

Expand Down
19 changes: 19 additions & 0 deletions src/site/apt/usage.apt.vm
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,22 @@ mvn package
the {{{/guides/introduction/introduction-to-the-lifecycle.html}Maven Build Life Cycle}}.

For full documentation, click {{{./plugin-info.html}here}}.

* Modular JAR files

The {{{https://openjdk.java.net/projects/jigsaw/spec/}Java Platform Module System (JPMS)}} introduced
{{{https://cr.openjdk.java.net/~mr/jigsaw/spec/jar.html}changes in the JAR file specifications}}
as well - Modular JAR files. Modular JAR files are JAR files with <<<module-info.class>>> file in the root directory
(or in the versioned area for multi-release JAR files). If the project contains <<<module-info.class>>>
the resulting JAR will be a Modular JAR without a need for any configuration regardless of the plugin version used.

Starting with version 3.1.2, if the JAR file contains <<<module-info.class>>>, this plugin will update
the modular descriptor (<<<module-info.class>>>) with additional attributes such as the list of packages included
and will validate the resulting modular descriptor (for example if all services provided by the module are actually
included in the JAR file). The most notable additional attribute added is the module main class.
If the JAR manifest contains <<<Main-Class>>> attribute this plugin will set the module main class to the same value
(for example how to set it read {{{../../shared/maven-archiver/examples/classpath.html}Make The Jar Executable}}).
Internally the plugin uses the JDK <<<jar>>> tool so in order to add the additional attributes and to
verify the modular descriptor Maven should be run with JDK version 9 or newer. If version 8 or earlier is used
the resulting JAR still will be Modular JAR (as it contains <<<module-info.class>>>) but no additional attributes
will be added and no validation will be performed.

0 comments on commit 78dbb29

Please sign in to comment.