org.devzendo.xplp.LauncherCreator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of CrossPlatformLauncherPlugin Show documentation
Show all versions of CrossPlatformLauncherPlugin Show documentation
A Maven plugin that creates launchers for Windows
(using Janel), Mac OS X (creating a .app structure, or tree with shell
script) or Linux (using a shell script).
(Apache License v2) 2008-2013 Matt Gumbley, DevZendo.org
The newest version!
/**
* Copyright (C) 2008-2010 Matt Gumbley, DevZendo.org
*
* 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.devzendo.xplp;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.CommandLineUtils.StringStreamConsumer;
/**
* A LauncherCreator is given all the parameters, extracted from
* the plugin configuration, and creates an appropriate
* launcher filesystem structure under the output directory.
*
* @author matt
*
*/
public abstract class LauncherCreator {
private final AbstractMojo mMojo;
private final File mOutputDirectory;
private final String mMainClassName;
private final String mApplicationName;
private final String mLibraryDirectory;
private final Set mTransitiveArtifacts;
private final Set mResourceDirectories;
private final Properties mParameterProperties;
private final String[] mSystemProperties;
private final String[] mVmArguments;
private final String[] mNarClassifierTypes;
/**
* @param mojo the parent mojo class
* @param outputDirectory where to create the .app structure
* @param mainClassName the main class
* @param applicationName the name of the application
* @param libraryDirectory where the libraries are stored
* @param transitiveArtifacts the set of transitive artifact dependencies
* @param resourceDirectories the project's resource directories
* @param parameterProperties the plugin configuration parameters, as properties
* @param systemProperties an array of name=value system properties
* @param vmArguments an array of arguments to the VM
* @param narClassifierTypes an array of NAR classifier:types
*/
public LauncherCreator(final AbstractMojo mojo,
final File outputDirectory,
final String mainClassName,
final String applicationName,
final String libraryDirectory,
final Set transitiveArtifacts,
final Set resourceDirectories,
final Properties parameterProperties,
final String[] systemProperties,
final String[] vmArguments,
final String[] narClassifierTypes) {
mMojo = mojo;
mOutputDirectory = outputDirectory;
mMainClassName = mainClassName;
mApplicationName = applicationName;
mLibraryDirectory = libraryDirectory;
mTransitiveArtifacts = transitiveArtifacts;
mResourceDirectories = resourceDirectories;
mParameterProperties = parameterProperties;
mSystemProperties = systemProperties;
mVmArguments = vmArguments;
mNarClassifierTypes = narClassifierTypes;
}
/**
* @return the mojo that Austin Powers stole
*/
protected final AbstractMojo getMojo() {
return mMojo;
}
/**
* @return the transitiveArtifacts
*/
protected final Set getTransitiveArtifacts() {
return mTransitiveArtifacts;
}
/**
* @return the outputDirectory
*/
protected final File getOutputDirectory() {
return mOutputDirectory;
}
/**
* @return the mainClassName
*/
protected final String getMainClassName() {
return mMainClassName;
}
/**
* @return the applicationName
*/
protected final String getApplicationName() {
return mApplicationName;
}
/**
* @return the libraryDirectory
*/
protected final String getLibraryDirectory() {
return mLibraryDirectory;
}
/**
* @return the resourceDirectories
*/
protected final Set getResourceDirectories() {
return mResourceDirectories;
}
/**
* @return the parameterProperties
*/
protected final Properties getParameterProperties() {
return mParameterProperties;
}
/**
* @return the array of system properties name=value pair strings
*/
protected final String[] getSystemProperties() {
return mSystemProperties;
}
/**
* @return the array of VM arguments
*/
protected final String[] getVmArguments() {
return mVmArguments;
}
/**
* Create a launcher specific to a given platform.
* @throws IOException on creation failure
*/
public abstract void createLauncher() throws IOException;
/**
* Copy a resource from the plugin's resources to a file.
* @param resourceName the name of the resource
* @param destinationFile the file to write it to
* @throws IOException on write failure
*/
protected final void copyPluginResource(final String resourceName, final File destinationFile) throws IOException {
final InputStream resourceAsStream = getPluginResourceAsStream(resourceName);
if (resourceAsStream == null) {
final String message = "Could not open resource " + resourceName;
mMojo.getLog().warn(message);
throw new IOException(message);
}
final OutputStream outputStream = createFileOutputStream(destinationFile);
final long bytesCopied = copyStream(resourceName, destinationFile.getAbsolutePath(),
resourceAsStream, outputStream);
mMojo.getLog().info("Created " + destinationFile.getAbsolutePath() + " [" + bytesCopied + " byte(s)]");
}
/**
* Copy a resource from the user project to a file
* @param resourceName the name of the resource in the user project
* @param destinationFile the file to write it to
* @throws IOException on write failure
*/
protected final void copyProjectResource(final String resourceName, final File destinationFile) throws IOException {
final InputStream resourceAsStream = getProjectResourceAsStream(resourceName);
final OutputStream outputStream = createFileOutputStream(destinationFile);
final long bytesCopied = copyStream(resourceName, destinationFile.getAbsolutePath(),
resourceAsStream, outputStream);
mMojo.getLog().info("Created " + destinationFile.getAbsolutePath() + " [" + bytesCopied + " byte(s)]");
}
private OutputStream createFileOutputStream(final File destinationFile) throws IOException {
try {
return new FileOutputStream(destinationFile);
} catch (final FileNotFoundException e) {
final String message = "Could not create destination file " + destinationFile.getAbsolutePath() + ": " + e.getMessage();
mMojo.getLog().warn(message);
throw new IOException(message);
}
}
private InputStream getProjectResourceAsStream(final String resourceName) throws IOException {
for (final File resourceDir : mResourceDirectories) {
try {
final InputStream resourceAsStream = new FileInputStream(new File(resourceDir, resourceName));
mMojo.getLog().debug("Located resource " + resourceName + " in directory " + resourceDir.getAbsolutePath());
return resourceAsStream;
} catch (final FileNotFoundException e) {
mMojo.getLog().debug("Resource " + resourceName + " not found in " + resourceDir.getAbsolutePath());
}
}
final String message = "Could not open resource " + resourceName;
mMojo.getLog().warn(message);
throw new IOException(message);
}
private InputStream getPluginResourceAsStream(final String resourceName) {
final InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName);
return resourceAsStream;
}
/**
* Find all the compile-scoped .jar or .nar artifacts in a set, and ensure
* they're returned as .jars ('cos that's what they are).
*
* @param transitiveArtifacts a set of artifacts
* @return the names of the filtered, transformed artifacts
*/
protected Set getTransitiveJarOrNarArtifactFileNames(final Set transitiveArtifacts) {
final Set jarNarArtifacts = new HashSet();
for (final Artifact transitiveArtifact : transitiveArtifacts) {
if (transitiveArtifact.getScope().equals("compile")
&& (transitiveArtifact.getType().equals("jar")
|| transitiveArtifact.getType().equals("nar"))) {
final String artifactFileName = transitiveArtifact.getFile().getName().replaceFirst("\\.nar$", ".jar");
jarNarArtifacts.add(artifactFileName);
}
}
return jarNarArtifacts;
}
/**
* Copy a file from its source to a destination directory.
* @param sourceFile the source file
* @param destinationDirectory the destination directory, which
* must exist
* @throws IOException on copy failure
*/
protected final void copyFile(final File sourceFile, final File destinationDirectory) throws IOException {
final String destinationFileName = sourceFile.getName();
copyFileWithRename(sourceFile, destinationDirectory,
destinationFileName);
}
private void copyFileWithRename(
final File sourceFile,
final File destinationDirectory,
final String destinationFileName) throws IOException {
InputStream inputStream;
try {
inputStream = new FileInputStream(sourceFile);
} catch (final IOException ioe) {
final String warning = "Could not open source file '" + sourceFile + "': " + ioe.getMessage();
mMojo.getLog().warn(warning);
throw new IOException(warning);
}
final File destinationFile = new File(destinationDirectory, destinationFileName);
OutputStream outputStream;
try {
outputStream = new FileOutputStream(destinationFile);
} catch (final FileNotFoundException e) {
final String message = "Could not create destination file " + destinationFile.getAbsolutePath() + ": " + e.getMessage();
mMojo.getLog().warn(message);
throw new IOException(message);
}
final long bytesCopied = copyStream(sourceFile.getAbsolutePath(), destinationFile.getAbsolutePath(),
inputStream, outputStream);
mMojo.getLog().info("Created "
+ destinationDirectory.getAbsoluteFile()
+ File.separatorChar + destinationFileName
+ " from " + sourceFile.getAbsolutePath()
+ " [" + bytesCopied + " byte(s) copied]");
}
private long copyStream(final String inName, final String outName,
final InputStream inputStream, final OutputStream outputStream)
throws IOException {
final int bufsize = 16384;
final byte[] buf = new byte[bufsize];
long totalRead = 0;
int nRead;
try {
while ((nRead = inputStream.read(buf, 0, bufsize)) != -1) {
outputStream.write(buf, 0, nRead);
totalRead += nRead;
}
return totalRead;
} catch (final IOException e) {
final String message = "Could not copy " + inName + " to "
+ outName + ": " + e.getMessage();
mMojo.getLog().warn(message);
throw new IOException(message);
} finally {
try {
inputStream.close();
} catch (final IOException ioe) {
}
try {
outputStream.close();
} catch (final IOException ioe) {
}
}
}
/**
* Copy all compile-scoped jar-typed transitive artifacts into
* a destination directory.
* @param destinationDirectory the destination directory, which
* must exist
* @throws IOException on copy failure
*/
protected final void copyTransitiveArtifacts(final File destinationDirectory) throws IOException {
getMojo().getLog().info("Copying transitive artifacts");
final Set transitiveArtifacts = getTransitiveArtifacts();
getMojo().getLog().info("There are " + transitiveArtifacts.size() + " transitive artifacts");
for (final Artifact artifact : transitiveArtifacts) {
if (artifact.getScope().equals("compile")) {
copyTransitiveArtifact(destinationDirectory, artifact);
} else {
getMojo().getLog().info("Not copying transitive artifact " + artifact + " since it is not a compile-scoped artifact");
}
}
}
private void copyTransitiveArtifact(
final File destinationDirectory,
final Artifact artifact) throws IOException {
if (!(artifact.getType().equals("jar") || artifact.getType().equals("nar"))) {
getMojo().getLog().info("Not copying transitive artifact " + artifact + " since it is not a jar or nar artifact");
return;
}
getMojo().getLog().info("Copying transitive artifact " + artifact);
final File artifactFile = artifact.getFile();
if (artifactFile.exists() && artifactFile.isFile()) {
if (artifact.getType().equals("nar")) {
copyTransitiveNarArtifact(artifact, destinationDirectory);
} else {
copyFile(artifactFile, destinationDirectory);
}
} else {
getMojo().getLog().warn("Not copying transitive artifact " + artifact + " since it either doesn't exist or is not a file");
getMojo().getLog().info("(Perhaps you're running this from an IDE, and this artifact is resolved as a project in your");
getMojo().getLog().info(" workspace and therefore on your classpath by the IDE?)");
}
}
private void copyTransitiveNarArtifact(
final Artifact artifact, final File destinationDirectory) throws IOException {
final File artifactFile = artifact.getFile();
final String destinationJarFileName = artifactFile.getName().replace(".nar", ".jar");
copyFileWithRename(artifactFile, destinationDirectory, destinationJarFileName);
for (final String narClassifierType : mNarClassifierTypes) {
final String[] split = narClassifierType.split(":");
copyNarClassifierType(artifact, destinationDirectory, split[0], split[1]);
}
}
private void copyNarClassifierType(
final Artifact artifact,
final File destinationDirectory,
final String classifier,
final String type) throws IOException {
getMojo().getLog().info("Copying unpacked NAR files for artifact: " + artifact);
final File unpackedNarDirectory = new File(mOutputDirectory, "nar/lib/" + classifier + "/" + type);
if (!unpackedNarDirectory.exists() || !unpackedNarDirectory.isDirectory()) {
getMojo().getLog().warn(
"NAR unpacked library directory " + unpackedNarDirectory.getAbsolutePath()
+ " for classifier:type " + classifier + ":" + type + " does not exist or is not a directory");
getMojo().getLog().warn("Did you forget to use the nar-assembly goal?");
return;
}
final File[] unpackedNarFiles = unpackedNarDirectory.listFiles();
for (final File file : unpackedNarFiles) {
final String name = file.getName();
getMojo().getLog().info("Considering NAR unpacked file: " + file.getAbsolutePath());
// Filter out files that are not library files, but unsure what the set
// of library files are: .jnilib, .so, .dll - but are there others?
if (name.equals("history.xml")) { // any other metadata files?
getMojo().getLog().info("Not copying " + name + " as it is NAR metadata");
continue;
}
final boolean isAnArtifactFile = name.contains(artifact.getArtifactId()) && name.contains(artifact.getVersion());
if (!isAnArtifactFile) {
getMojo().getLog().warn("Not copying " + name + " as it does not refer to the artifact " + artifact);
continue;
}
copyFile(file, destinationDirectory);
}
}
/**
* Copy a user project resource to a file, interpolating with the configuration properties
* @param resourceName the name of the resource in the user project
* @param outputFile the file to write it to
* @throws IOException on write failure
*/
protected final void copyInterpolatedProjectResource(final String resourceName, final File outputFile) throws IOException {
final BufferedReader br = new BufferedReader(new InputStreamReader(getProjectResourceAsStream(resourceName)));
copyInterpolatedResource(resourceName, outputFile, br);
}
/**
* Copy a plugin resource to a file, interpolating with the configuration properties
* @param resourceName the name of the plugin resource
* @param outputFile the file to write it to
* @throws IOException on write failure
*/
protected final void copyInterpolatedPluginResource(final String resourceName, final File outputFile) throws IOException {
final BufferedReader br = new BufferedReader(new InputStreamReader(getPluginResourceAsStream(resourceName)));
copyInterpolatedResource(resourceName, outputFile, br);
}
private void copyInterpolatedResource(
final String resourceName,
final File outputFile,
final BufferedReader br) throws IOException {
long bytesCopied = 0;
final PropertiesInterpolator interpolator = new PropertiesInterpolator(getParameterProperties());
try {
final String lineSeparator = System.getProperty("line.separator");
final FileWriter fw = createFileWriter(outputFile);
try {
while (true) {
final String line = br.readLine();
if (line == null) {
break;
}
try {
final String outLine = interpolator.interpolate(line);
fw.write(outLine);
bytesCopied += outLine.length();
if (!outLine.endsWith(lineSeparator)) {
fw.write(lineSeparator);
bytesCopied += lineSeparator.length();
}
} catch (final IllegalStateException e) {
final String message = "Cannot interpolate whilst processing " + resourceName + ": " + e.getMessage();
getMojo().getLog().warn(message);
throw new IOException(message);
}
}
} finally {
try {
fw.close();
} catch (final IOException e) {
// nothing
}
}
} finally {
try {
br.close();
} catch (final IOException e) {
// nothing
}
}
getMojo().getLog().info("Created "
+ outputFile.getAbsolutePath()
+ " [" + bytesCopied + " byte(s) copied]");
}
private FileWriter createFileWriter(final File outputFile) throws IOException {
try {
return new FileWriter(outputFile);
} catch (final IOException e) {
final String message = "Could not create destination file "
+ outputFile.getAbsolutePath() + ": " + e.getMessage();
mMojo.getLog().warn(message);
throw new IOException(message);
}
}
/**
* Make a file executable, in the terms of the specific platform.
* @param nonExecutableFile some file that may not currently be executable
*/
protected final void makeExecutable(final File nonExecutableFile) {
getMojo().getLog().info("Making " + nonExecutableFile + " executable");
final Commandline cl = new Commandline("chmod");
cl.addArguments(new String [] {"a+x" , nonExecutableFile.getAbsolutePath()});
try {
final StringStreamConsumer output = new StringStreamConsumer();
final StringStreamConsumer error = new StringStreamConsumer();
final int returnValue = CommandLineUtils.executeCommandLine(cl, output, error);
if (returnValue != 0) {
getMojo().getLog().warn("chmod exit code is " + returnValue);
getMojo().getLog().warn("chmod output: " + output.getOutput());
getMojo().getLog().warn("chmod error output: " + error.getOutput());
}
} catch (final CommandLineException e) {
getMojo().getLog().warn("Couldn't run chmod: " + e.getMessage());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy