Further maven module restructure

This commit is contained in:
Phillip Webb
2013-07-08 11:41:51 -07:00
parent 9fde0a3715
commit cd51f357a3
420 changed files with 92 additions and 92 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.zero.maven.it</groupId>
<artifactId>executable-jar</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>executable-jar</packaging>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,8 @@
package org.test;
public class SampleApplication {
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,7 @@
import java.io.*;
import org.springframework.zero.maven.*;
Verify.verifyJar(
new File( basedir, "target/executable-jar-0.0.1.BUILD-SNAPSHOT.jar" )
);

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.zero.maven.it</groupId>
<artifactId>executable-war</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>executable-war</packaging>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,8 @@
package org.test;
public class SampleApplication {
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1 @@
<html></html>

View File

@@ -0,0 +1,7 @@
import java.io.*;
import org.springframework.zero.maven.*;
Verify.verifyWar(
new File( basedir, "target/executable-war-0.0.1.BUILD-SNAPSHOT.war" )
);

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<profiles>
<profile>
<id>it-repo</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>local.central</id>
<url>@localRepositoryUrl@</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>local.central</id>
<url>@localRepositoryUrl@</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>

View File

@@ -0,0 +1,334 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.archiver.zip.ZipEntry;
import org.codehaus.plexus.archiver.zip.ZipFile;
import org.codehaus.plexus.archiver.zip.ZipResource;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.resolution.ArtifactDescriptorRequest;
import org.sonatype.aether.resolution.ArtifactDescriptorResult;
import org.sonatype.aether.resolution.ArtifactRequest;
import org.sonatype.aether.resolution.ArtifactResult;
import org.sonatype.aether.util.artifact.DefaultArtifact;
/**
* Abstract base of MOJOs that package executable archives.
*
* @author Phillip Webb
*/
public abstract class AbstractExecutableArchiveMojo extends AbstractMojo {
private static final String[] DEFAULT_EXCLUDES = new String[] { "**/package.html" };
private static final String[] DEFAULT_INCLUDES = new String[] { "**/**" };
private static final String MAIN_CLASS_ATTRIBUTE = "Main-Class";
private static final String START_CLASS_ATTRIBUTE = "Start-Class";
/**
* Archiver used to create a JAR file.
*/
@Component(role = Archiver.class, hint = "jar")
private JarArchiver jarArchiver;
/**
* Maven project helper utils.
*/
@Component
private MavenProjectHelper projectHelper;
/**
* Aether repository system used to download artifacts.
*/
@Component
private RepositorySystem repositorySystem;
/**
* The Maven project.
*/
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
/**
* The Maven session.
*/
@Parameter(defaultValue = "${session}", readonly = true, required = true)
private MavenSession session;
/**
* Directory containing the generated archive.
*/
@Parameter(defaultValue = "${project.build.directory}", required = true)
private File outputDirectory;
/**
* Name of the generated archive.
*/
@Parameter(defaultValue = "${project.build.finalName}", required = true)
private String finalName;
/**
* The name of the main class. If not specified the first compiled class found that
* contains a 'main' method will be used.
*/
@Parameter
private String mainClass;
/**
* Classifier to add to the artifact generated. If given, the artifact will be
* attached. If this is not given,it will merely be written to the output directory
* according to the finalName.
*/
@Parameter
private String classifier;
/**
* List of files to include. Specified as fileset patterns which are relative to the
* input directory whose contents is being packaged into the archive.
*/
@Parameter
private String[] includes;
/**
* List of files to exclude. Specified as fileset patterns which are relative to the
* input directory whose contents is being packaged into the archive.
*/
@Parameter
private String[] excludes;
/**
* Directory containing the classes and resource files that should be packaged into
* the archive.
*/
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
private File classesDirectrory;
/**
* The archive configuration to use. See <a
* href="http://maven.apache.org/shared/maven-archiver/index.html">Maven Archiver
* Reference</a>.
*/
@Parameter
private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
/**
* Whether creating the archive should be forced.
*/
@Parameter(property = "archive.forceCreation", defaultValue = "false")
private boolean forceCreation;
/**
* The current repository/network configuration of Maven.
*/
@Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
private RepositorySystemSession repositorySystemSession;
/**
* Returns the type as defined in plexus components.xml
*/
protected abstract String getType();
/**
* Returns the file extension for the archive (e.g. 'jar').
*/
protected abstract String getExtension();
/**
* Returns the destination of an {@link Artifact}.
* @param artifact the artifact
* @return the destination or {@code null} to exclude
*/
protected abstract String getArtifactDestination(Artifact artifact);
/**
* Returns the launcher class that will be used.
*/
protected abstract String getLauncherClass();
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
File archiveFile = createArchive();
if (this.classifier == null || this.classifier.isEmpty()) {
this.project.getArtifact().setFile(archiveFile);
}
else {
this.projectHelper.attachArtifact(this.project, getType(), this.classifier,
archiveFile);
}
}
private File createArchive() throws MojoExecutionException {
File archiveFile = getTargetFile();
MavenArchiver archiver = new MavenArchiver();
customizeArchiveConfiguration();
archiver.setArchiver(this.jarArchiver);
archiver.setOutputFile(archiveFile);
archiver.getArchiver().setRecompressAddedZips(false);
try {
addContent(archiver);
addLibs(archiver);
ZipFile zipFile = addLauncherClasses(archiver);
try {
archiver.createArchive(this.session, this.project, this.archive);
return archiveFile;
}
finally {
zipFile.close();
}
}
catch (Exception ex) {
throw new MojoExecutionException("Error assembling archive", ex);
}
}
private File getTargetFile() {
String classifier = (this.classifier == null ? "" : this.classifier.trim());
if (classifier.length() > 0 && !classifier.startsWith("-")) {
classifier = "-" + classifier;
}
return new File(this.outputDirectory, this.finalName + classifier + "."
+ getExtension());
}
private void customizeArchiveConfiguration() throws MojoExecutionException {
this.archive.setForced(this.forceCreation);
String mainClass = this.mainClass;
if (mainClass == null) {
mainClass = this.archive.getManifestEntries().get(MAIN_CLASS_ATTRIBUTE);
}
if (mainClass == null) {
mainClass = MainClassFinder.findMainClass(this.classesDirectrory);
}
if (mainClass == null) {
throw new MojoExecutionException("Unable to find a suitable main class, "
+ "please add a 'mainClass' property");
}
this.archive.getManifestEntries().put(MAIN_CLASS_ATTRIBUTE, getLauncherClass());
this.archive.getManifestEntries().put(START_CLASS_ATTRIBUTE, mainClass);
}
protected void addContent(MavenArchiver archiver) {
if (this.classesDirectrory.exists()) {
archiver.getArchiver().addDirectory(this.classesDirectrory,
getClassesDirectoryPrefix(), getIncludes(), getExcludes());
}
}
protected String getClassesDirectoryPrefix() {
return "";
}
protected final String[] getIncludes() {
if (this.includes != null && this.includes.length > 0) {
return this.includes;
}
return DEFAULT_INCLUDES;
}
protected final String[] getExcludes() {
if (this.excludes != null && this.excludes.length > 0) {
return this.excludes;
}
return DEFAULT_EXCLUDES;
}
private void addLibs(MavenArchiver archiver) {
for (Artifact artifact : this.project.getArtifacts()) {
if (artifact.getFile() != null) {
String dir = getArtifactDestination(artifact);
if (dir != null) {
archiver.getArchiver().addFile(artifact.getFile(),
dir + artifact.getFile().getName());
}
}
}
}
private ZipFile addLauncherClasses(MavenArchiver archiver)
throws MojoExecutionException {
try {
List<RemoteRepository> repositories = new ArrayList<RemoteRepository>();
repositories.addAll(this.project.getRemotePluginRepositories());
repositories.addAll(this.project.getRemoteProjectRepositories());
String version = getClass().getPackage().getImplementationVersion();
DefaultArtifact artifact = new DefaultArtifact(
"org.springframework.zero:spring-launcher:" + version);
ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest(
artifact, repositories, "plugin");
ArtifactDescriptorResult descriptorResult = this.repositorySystem
.readArtifactDescriptor(this.repositorySystemSession,
descriptorRequest);
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setRepositories(repositories);
artifactRequest.setArtifact(descriptorResult.getArtifact());
ArtifactResult artifactResult = this.repositorySystem.resolveArtifact(
this.repositorySystemSession, artifactRequest);
if (artifactResult.getArtifact() == null) {
throw new MojoExecutionException("Unable to resolve launcher classes");
}
return addLauncherClasses(archiver, artifactResult.getArtifact().getFile());
}
catch (Exception ex) {
if (ex instanceof MojoExecutionException) {
throw (MojoExecutionException) ex;
}
throw new MojoExecutionException("Unable to add launcher classes", ex);
}
}
private ZipFile addLauncherClasses(MavenArchiver archiver, File file)
throws IOException {
ZipFile zipFile = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (!entry.isDirectory() && !entry.getName().startsWith("/META-INF")) {
ZipResource zipResource = new ZipResource(zipFile, entry);
archiver.getArchiver().addResource(zipResource, entry.getName(), -1);
}
}
return zipFile;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.ResolutionScope;
/**
* Build an executable JAR file.
*
* @author Phillip Webb
*/
@Mojo(name = "executable-jar", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class ExecutableJarMojo extends AbstractExecutableArchiveMojo {
private static final Set<String> LIB_SCOPES = new HashSet<String>(Arrays.asList(
"compile", "runtime", "provided"));
@Override
protected String getType() {
return "executable-jar";
}
@Override
protected String getExtension() {
return "jar";
}
@Override
protected String getArtifactDestination(Artifact artifact) {
if (LIB_SCOPES.contains(artifact.getScope())) {
return "lib/";
}
return null;
}
@Override
protected String getLauncherClass() {
return "org.springframework.zero.launcher.JarLauncher";
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.maven.archiver.MavenArchiver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
/**
* Build an executable WAR file.
*
* @author Phillip Webb
*/
@Mojo(name = "executable-war", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class ExecutableWarMojo extends AbstractExecutableArchiveMojo {
// FIXME classes in the wrong place
private static final Map<String, String> SCOPE_DESTINATIONS;
static {
Map<String, String> map = new HashMap<String, String>();
map.put("compile", "WEB-INF/lib/");
map.put("runtime", "WEB-INF/lib/");
map.put("provided", "WEB-INF/lib-provided/");
SCOPE_DESTINATIONS = Collections.unmodifiableMap(map);
}
/**
* Single directory for extra files to include in the WAR. This is where you place
* your JSP files.
*/
@Parameter(defaultValue = "${basedir}/src/main/webapp", required = true)
private File warSourceDirectory;
@Override
protected String getType() {
return "executable-war";
}
@Override
protected String getExtension() {
return "war";
}
@Override
protected String getArtifactDestination(Artifact artifact) {
return SCOPE_DESTINATIONS.get(artifact.getScope());
}
@Override
protected String getClassesDirectoryPrefix() {
return "WEB-INF/classes/";
}
@Override
protected void addContent(MavenArchiver archiver) {
super.addContent(archiver);
if (this.warSourceDirectory.exists()) {
archiver.getArchiver().addDirectory(this.warSourceDirectory, getIncludes(),
getExcludes());
}
}
@Override
protected String getLauncherClass() {
return "org.springframework.zero.launcher.WarLauncher";
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Deque;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* Finds any class with a {@code public static main} method by performing a breadth first
* directory search.
*
* @author Phillip Webb
*/
abstract class MainClassFinder {
private static final String DOT_CLASS = ".class";
private static final Type STRING_ARRAY_TYPE = Type.getType(String[].class);
private static final Type MAIN_METHOD_TYPE = Type.getMethodType(Type.VOID_TYPE,
STRING_ARRAY_TYPE);
private static final String MAIN_METHOD_NAME = "main";
private static final FileFilter CLASS_FILE_FILTER = new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(DOT_CLASS));
}
};
private static final FileFilter PACKAGE_FOLDER_FILTER = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() && !file.getName().startsWith(".");
}
};
public static String findMainClass(File root) {
File mainClassFile = findMainClassFile(root);
if (mainClassFile == null) {
return null;
}
String mainClass = mainClassFile.getAbsolutePath().substring(
root.getAbsolutePath().length() + 1);
mainClass = mainClass.replace('/', '.');
mainClass = mainClass.replace('\\', '.');
mainClass = mainClass.substring(0, mainClass.length() - DOT_CLASS.length());
return mainClass;
}
public static File findMainClassFile(File root) {
Deque<File> stack = new ArrayDeque<File>();
stack.push(root);
while (!stack.isEmpty()) {
File file = stack.pop();
if (isMainClassFile(file)) {
return file;
}
if (file.isDirectory()) {
pushAllSorted(stack, file.listFiles(PACKAGE_FOLDER_FILTER));
pushAllSorted(stack, file.listFiles(CLASS_FILE_FILTER));
}
}
return null;
}
private static boolean isMainClassFile(File file) {
try {
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
try {
ClassReader classReader = new ClassReader(inputStream);
MainMethodFinder mainMethodFinder = new MainMethodFinder();
classReader.accept(mainMethodFinder, ClassReader.SKIP_CODE);
return mainMethodFinder.isFound();
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
return false;
}
}
private static void pushAllSorted(Deque<File> stack, File[] files) {
Arrays.sort(files, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
return o1.getName().compareTo(o2.getName());
}
});
for (File file : files) {
stack.push(file);
}
}
private static class MainMethodFinder extends ClassVisitor {
private boolean found;
public MainMethodFinder() {
super(Opcodes.ASM4);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if (isAccess(access, Opcodes.ACC_PUBLIC, Opcodes.ACC_STATIC)
&& MAIN_METHOD_NAME.equals(name)
&& MAIN_METHOD_TYPE.getDescriptor().equals(desc)) {
this.found = true;
}
return null;
}
private boolean isAccess(int access, int... requiredOpsCodes) {
for (int requiredOpsCode : requiredOpsCodes) {
if ((access & requiredOpsCode) == 0) {
return false;
}
}
return true;
}
public boolean isFound() {
return this.found;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.apache.maven.plugins.shade.relocation.Relocator;
import org.apache.maven.plugins.shade.resource.ResourceTransformer;
/**
* Extension for the <a href="http://maven.apache.org/plugins/maven-shade-plugin/">Maven
* shade plugin</a> to allow properties files (e.g. <code>META-INF/spring.factories</code>
* ) to be merged without losing any information.
*
* @author Dave Syer
*/
public class PropertiesMergingResourceTransformer implements ResourceTransformer {
// FIXME move out of core
private String resource; // Set this in pom configuration with
// <resource>...</resource>
private Properties data = new Properties();
/**
* @return the data the properties being merged
*/
public Properties getData() {
return this.data;
}
@Override
public boolean canTransformResource(String resource) {
if (this.resource != null && this.resource.equalsIgnoreCase(resource)) {
return true;
}
return false;
}
@Override
public void processResource(String resource, InputStream is,
List<Relocator> relocators) throws IOException {
Properties properties = new Properties();
properties.load(is);
is.close();
for (Object key : properties.keySet()) {
String name = (String) key;
String value = properties.getProperty(name);
String existing = this.data.getProperty(name);
this.data
.setProperty(name, existing == null ? value : existing + "," + value);
}
}
@Override
public boolean hasTransformedResource() {
return this.data.size() > 0;
}
@Override
public void modifyOutputStream(JarOutputStream os) throws IOException {
os.putNextEntry(new JarEntry(this.resource));
this.data.store(os, "Merged by PropertiesMergingResourceTransformer");
os.flush();
this.data.clear();
}
public String getResource() {
return this.resource;
}
public void setResource(String resource) {
this.resource = resource;
}
}

View File

@@ -0,0 +1,74 @@
<component-set>
<components>
<component>
<role>org.apache.maven.artifact.handler.ArtifactHandler</role>
<role-hint>executable-war</role-hint>
<implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler
</implementation>
<configuration>
<type>executable-jar</type>
<extension>jar</extension>
<language>java</language>
<addedToClasspath>true</addedToClasspath>
</configuration>
</component>
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>executable-jar</role-hint>
<implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping
</implementation>
<configuration>
<lifecycles>
<lifecycle>
<id>default</id>
<phases>
<process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
<compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>
<process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>
<test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>
<test>org.apache.maven.plugins:maven-surefire-plugin:test</test>
<package>org.springframework.zero:spring-package-maven-plugin:executable-jar</package>
<install>org.apache.maven.plugins:maven-install-plugin:install</install>
<deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
</phases>
</lifecycle>
</lifecycles>
</configuration>
</component>
<component>
<role>org.apache.maven.artifact.handler.ArtifactHandler</role>
<role-hint>executable-war</role-hint>
<implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler
</implementation>
<configuration>
<type>executable-war</type>
<extension>war</extension>
<language>java</language>
<addedToClasspath>true</addedToClasspath>
</configuration>
</component>
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>executable-war</role-hint>
<implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping
</implementation>
<configuration>
<lifecycles>
<lifecycle>
<id>default</id>
<phases>
<process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
<compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>
<process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>
<test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>
<test>org.apache.maven.plugins:maven-surefire-plugin:test</test>
<package>org.springframework.zero:spring-package-maven-plugin:executable-war</package>
<install>org.apache.maven.plugins:maven-install-plugin:install</install>
<deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
</phases>
</lifecycle>
</lifecycles>
</configuration>
</component>
</components>
</component-set>

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.codehaus.plexus.util.IOUtil;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.zero.maven.sample.ClassWithMainMethod;
import org.springframework.zero.maven.sample.ClassWithoutMainMethod;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link MainClassFinder}.
*
* @author Phillip Webb
*/
public class MainClassFinderTests {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void findMainClass() throws Exception {
File expected = copyToTemp("b.class", ClassWithMainMethod.class);
copyToTemp("a.class", ClassWithoutMainMethod.class);
File actual = MainClassFinder.findMainClassFile(this.temporaryFolder.getRoot());
assertThat(actual, equalTo(expected));
}
@Test
public void findMainClassInSubFolder() throws Exception {
File expected = copyToTemp("a/b/c/d.class", ClassWithMainMethod.class);
copyToTemp("a/b/c/e.class", ClassWithoutMainMethod.class);
copyToTemp("a/b/f.class", ClassWithoutMainMethod.class);
File actual = MainClassFinder.findMainClassFile(this.temporaryFolder.getRoot());
assertThat(actual, equalTo(expected));
}
@Test
public void usesBreadthFirst() throws Exception {
File expected = copyToTemp("a/b.class", ClassWithMainMethod.class);
copyToTemp("a/b/c/e.class", ClassWithMainMethod.class);
File actual = MainClassFinder.findMainClassFile(this.temporaryFolder.getRoot());
assertThat(actual, equalTo(expected));
}
@Test
public void findsClassName() throws Exception {
copyToTemp("org/test/MyApp.class", ClassWithMainMethod.class);
assertThat(MainClassFinder.findMainClass(this.temporaryFolder.getRoot()),
equalTo("org.test.MyApp"));
}
private File copyToTemp(String filename, Class<?> classToCopy) throws IOException {
String[] paths = filename.split("\\/");
File file = this.temporaryFolder.getRoot();
for (String path : paths) {
file = new File(file, path);
}
file.getParentFile().mkdirs();
InputStream inputStream = getClass().getResourceAsStream(
"/" + classToCopy.getName().replace(".", "/") + ".class");
OutputStream outputStream = new FileOutputStream(file);
try {
IOUtil.copy(inputStream, outputStream);
}
finally {
outputStream.close();
}
return file;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.jar.JarOutputStream;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link PropertiesMergingResourceTransformer}.
*
* @author Dave Syer
*/
public class PropertiesMergingResourceTransformerTests {
private PropertiesMergingResourceTransformer transformer = new PropertiesMergingResourceTransformer();
@Test
public void testProcess() throws Exception {
assertFalse(this.transformer.hasTransformedResource());
this.transformer.processResource("foo",
new ByteArrayInputStream("foo=bar".getBytes()), null);
assertTrue(this.transformer.hasTransformedResource());
}
@Test
public void testMerge() throws Exception {
this.transformer.processResource("foo",
new ByteArrayInputStream("foo=bar".getBytes()), null);
this.transformer.processResource("bar",
new ByteArrayInputStream("foo=spam".getBytes()), null);
assertEquals("bar,spam", this.transformer.getData().getProperty("foo"));
}
@Test
public void testOutput() throws Exception {
this.transformer.setResource("foo");
this.transformer.processResource("foo",
new ByteArrayInputStream("foo=bar".getBytes()), null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
JarOutputStream os = new JarOutputStream(out);
this.transformer.modifyOutputStream(os);
os.flush();
os.close();
assertNotNull(out.toByteArray());
assertTrue(out.toByteArray().length > 0);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven;
import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Verification utility for use with maven-invoker-plugin verification scripts.
*
* @author Phillip Webb
*/
public class Verify {
public static void verifyJar(File file) throws Exception {
new JarArchiveVerification(file).verify();
}
public static void verifyWar(File file) throws Exception {
new WarArchiveVerification(file).verify();
}
private static abstract class AbstractArchiveVerification {
private File file;
public AbstractArchiveVerification(File file) {
this.file = file;
}
public void verify() throws Exception {
assertTrue("Archive missing", this.file.exists());
assertTrue("Archive not a file", this.file.isFile());
ZipFile zipFile = new ZipFile(this.file);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
Map<String, ZipEntry> zipMap = new HashMap<String, ZipEntry>();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
zipMap.put(zipEntry.getName(), zipEntry);
}
verifyZipEntries(zipFile, zipMap);
zipFile.close();
}
protected void verifyZipEntries(ZipFile zipFile, Map<String, ZipEntry> entries)
throws Exception {
verifyManifest(zipFile, entries.get("META-INF/MANIFEST.MF"));
}
private void verifyManifest(ZipFile zipFile, ZipEntry zipEntry) throws Exception {
Manifest manifest = new Manifest(zipFile.getInputStream(zipEntry));
verifyManifest(manifest);
}
protected abstract void verifyManifest(Manifest manifest) throws Exception;
protected final void assertHasEntryNameStartingWith(
Map<String, ZipEntry> entries, String value) {
for (String name : entries.keySet()) {
if (name.startsWith(value)) {
return;
}
}
throw new IllegalStateException("Expected entry starting with " + value);
}
}
private static class JarArchiveVerification extends AbstractArchiveVerification {
public JarArchiveVerification(File file) {
super(file);
}
@Override
protected void verifyZipEntries(ZipFile zipFile, Map<String, ZipEntry> entries)
throws Exception {
super.verifyZipEntries(zipFile, entries);
assertHasEntryNameStartingWith(entries, "lib/spring-context");
assertHasEntryNameStartingWith(entries, "lib/spring-core");
assertHasEntryNameStartingWith(entries, "lib/javax.servlet-api-3.0.1.jar");
assertTrue("Unpacked launcher classes", entries.containsKey("org/"
+ "springframework/zero/launcher/JarLauncher.class"));
assertTrue("Own classes", entries.containsKey("org/"
+ "test/SampleApplication.class"));
}
@Override
protected void verifyManifest(Manifest manifest) throws Exception {
assertEquals("org.springframework.zero.launcher.JarLauncher", manifest
.getMainAttributes().getValue("Main-Class"));
assertEquals("org.test.SampleApplication", manifest.getMainAttributes()
.getValue("Start-Class"));
}
}
private static class WarArchiveVerification extends AbstractArchiveVerification {
public WarArchiveVerification(File file) {
super(file);
}
@Override
protected void verifyZipEntries(ZipFile zipFile, Map<String, ZipEntry> entries)
throws Exception {
super.verifyZipEntries(zipFile, entries);
assertHasEntryNameStartingWith(entries, "WEB-INF/lib/spring-context");
assertHasEntryNameStartingWith(entries, "WEB-INF/lib/spring-core");
assertHasEntryNameStartingWith(entries,
"WEB-INF/lib-provided/javax.servlet-api-3.0.1.jar");
assertTrue("Unpacked launcher classes", entries.containsKey("org/"
+ "springframework/zero/launcher/JarLauncher.class"));
assertTrue("Own classes", entries.containsKey("WEB-INF/classes/org/"
+ "test/SampleApplication.class"));
assertTrue("Web content", entries.containsKey("index.html"));
}
@Override
protected void verifyManifest(Manifest manifest) throws Exception {
assertEquals("org.springframework.zero.launcher.WarLauncher", manifest
.getMainAttributes().getValue("Main-Class"));
assertEquals("org.test.SampleApplication", manifest.getMainAttributes()
.getValue("Start-Class"));
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven.sample;
/**
* Sample class with a main method.
*
* @author Phillip Webb
*/
public class ClassWithMainMethod {
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed 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.
*/
package org.springframework.zero.maven.sample;
/**
* Sample class without a main method.
*
* @author Phillip Webb
*/
public class ClassWithoutMainMethod {
}