Java tutorial
/* * #%L * Mojo's Maven plugin for Cobertura * %% * Copyright (C) 2005 - 2013 Codehaus * %% * 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. * #L% */ package org.codehaus.mojo.cobertura.integration.shell; import net.sourceforge.cobertura.util.CommandLineBuilder; import org.apache.commons.lang.SystemUtils; import org.apache.commons.lang.Validate; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.logging.Log; import org.codehaus.plexus.util.cli.Commandline; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; /** * Plain shell argument line implementation of the JvmArgumentLine specification. * * @author <a href="mailto:lj@jguru.se">Lennart Jörelid</a>, jGuru Europe AB */ public class PlainJvmCommandLine implements JvmArgumentLine { // Internal state private Log mavenLog; private SortedSet<String> options; private List<String> arguments; private String mainClass; private List<Artifact> classpathEntries; private boolean useCommandFile; /** * Convenience constructor, creating a new PlainJvmCommandLine instance using the supplied mavenLog * and all other state initialized but empty. The mainClass will be {@code null} after firing this * constructor, though, and needs to be set by invoking {@code setMainClass} before executing this * PlainJvmCommandLine. * * @param mavenLog The non-null Log used to display messages as requested. */ public PlainJvmCommandLine(final Log mavenLog) { // Delegate this(mavenLog, new ArrayList<Artifact>(), new TreeSet<String>(), "completely.irrelevant", new ArrayList<String>(), true); // Assign internal state this.mainClass = null; } /** * Compound constructor creating a new PlainJvmCommandLine wrapping the supplied data. * * @param mavenLog The non-null Log used to display messages as requested. * @param classpathEntries A List holding all Artifacts to use as classpath entries for the Forked JVM. * @param options The command-line options to inject before the mainClass of this PlainJvmCommandLine. * @param mainClass The fully qualified class name of the class to be executed by this PlainJvmCommandLine. * @param arguments A List holding all arguments * @param useCommandsFile indicates if this PlainJvmCommandLine should use a Cobertura commandsFile to write * all arguments to. */ public PlainJvmCommandLine(final Log mavenLog, final List<Artifact> classpathEntries, final SortedSet<String> options, final String mainClass, final List<String> arguments, final boolean useCommandsFile) { // Check sanity Validate.notNull(mavenLog, "Cannot handle null mavenLog argument."); Validate.notNull(classpathEntries, "Cannot handle null classpathEntries argument."); Validate.notNull(options, "Cannot handle null options argument."); Validate.notNull(arguments, "Cannot handle null arguments argument."); // Assign internal state this.mavenLog = mavenLog; this.options = options; this.arguments = arguments; this.classpathEntries = classpathEntries; this.useCommandFile = useCommandsFile; setMainClass(mainClass); } /** * {@inheritDoc} */ public final void addOption(final String option) throws IllegalArgumentException { options.add(getTrimmedAndValidated(option, "option", true)); } /** * {@inheritDoc} */ public final void addOption(final String key, final String value) throws IllegalArgumentException { options.add(getTrimmedAndValidated(key, "option key", true) + "=" + getTrimmedAndValidated(value, "option value", false)); } /** * {@inheritDoc} */ public final void setMainClass(final String fullyQualifiedClassName) { mainClass = getTrimmedAndValidated(fullyQualifiedClassName, "mainClass", false); } /** * {@inheritDoc} */ public final String getMainClass() { return mainClass; } /** * {@inheritDoc} */ public void addToClasspath(final Artifact classpathArtifact) { // Check sanity Validate.notNull(classpathArtifact, "Cannot handle null classpathArtifact argument."); // Assign internal state, if applicable. if (!classpathEntries.contains(classpathArtifact)) { classpathEntries.add(classpathArtifact); } else { final StringBuilder builder = new StringBuilder(); if (classpathArtifact.getGroupId() != null) { builder.append(classpathArtifact.getGroupId()); } if (classpathArtifact.getArtifactId() != null) { builder.append(":").append(classpathArtifact.getArtifactId()); } if (classpathArtifact.getVersion() != null) { builder.append(":").append(classpathArtifact.getVersion()); } if (classpathArtifact.getType() != null) { builder.append(":").append(classpathArtifact.getType()); } mavenLog.warn("Not adding classpath artifact [" + builder.toString() + "] twice."); } } /** * {@inheritDoc} */ public void addArgument(final String argument) { arguments.add(getTrimmedAndValidated(argument, "argument", false)); } /** * {@inheritDoc} */ public void useCommandFile(boolean yes) { this.useCommandFile = yes; } /** * Default implementation of the CommandLine assigns the classpath as an * environment variable (i.e. {@code CLASSPATH}) to reduce the CommandLine * length, thereby catering for Windows shells and their problems with * long command lines ... * <p/> * {@inheritDoc} */ public Commandline validateStateAndRetrieveCommandLine() throws IllegalStateException { // Check sanity if (mainClass == null || mainClass.trim().equals("")) { throw new IllegalStateException( "Invalid null or empty MainClass. " + "(Must be set before retrieving a Commandline)."); } // Use Plexus Utils to generate a CommandLine which can be executed as a separate Java process. final Commandline cl = new Commandline(); final File java = new File(SystemUtils.getJavaHome(), "bin/java"); cl.setExecutable(java.getAbsolutePath()); cl.addEnvironment("CLASSPATH", synthesizeClasspath()); // Add all options to the CommandLine for (String current : options) { createArgWithValue(cl, current); } createArgWithValue(cl, mainClass); // Add all other arguments if (useCommandFile) { try { final File commandsFile = createCommandsFile(); // Add the commandsfile argument, which is on the form // // --commandsfile [path to file] // createArgWithValue(cl, "--commandsfile"); createArgWithValue(cl, commandsFile.getAbsolutePath()); } catch (IOException e) { e.printStackTrace(); } } else { // Simply add the arguments in order for (String arg : arguments) { createArgWithValue(cl, arg); } } // All done. return cl; } /** * Override this method in subclasses to modify the synthesized Classpath before use. * * @param synthesizedClasspath The classpath synthesized from all provided classpathArtifacts. * @return The final classpath to use in the forked Cobertura process. */ protected String modifyClasspath(final String synthesizedClasspath) { return synthesizedClasspath; } /** * Generate a Cobertura-compliant commands file, write all {@code arguments} into it and return its File object. * * @return the CommandsFile File instance. * @throws IOException if the command file could not be created. * @see net.sourceforge.cobertura.util.CommandLineBuilder#getCommandLineFile() */ protected File createCommandsFile() throws IOException { final CommandLineBuilder builder = new CommandLineBuilder(); for (String arg : arguments) { builder.addArg(arg); } // Save the command line file and validate that we succeeded. builder.saveArgs(); final File toReturn = new File(builder.getCommandLineFile()); if (!toReturn.exists()) { // Failed to create the commands file. Complain. throw new IOException("Failed creating commandsFile [" + toReturn.getCanonicalPath() + "]"); } // All done. return toReturn; } // // Private helpers // /** * Joins all classpathEntries into a proper classpath, as required by a forked Cobertura process. * * @return the Classpath string which could be used for invoking a forked Cobertura process. * @throws java.lang.IllegalStateException if any taskClasspathArtifact could not be resolved to a canonical * path (i.e. its JAR file could not be located, or equivalent). */ private String synthesizeClasspath() throws IllegalStateException { final StringBuilder builder = new StringBuilder(); for (Artifact current : classpathEntries) { try { // Get the path to the Artifact final String artifactPath = current.getFile().getCanonicalPath(); // Append the artifact file path to the builder. builder.append(File.pathSeparator).append(artifactPath); } catch (IOException e) { throw new IllegalStateException("Could not find canonical path for '" + current.getFile() + "'.", e); } } // All done. return modifyClasspath(builder.toString()); } /** * Validates the the supplied 'original' argument is neither null nor empty * (following trim()-ming of it). Returns a non-empty {@code trim()}-med * version of the original supplied. * * @param original The original argument strength. * @param type The type to describe the original argument in an exception message. * @param requireStartWithDash if {@code true}, the key must start with a '-'. * @return a non-empty {@code trim()}-med version of the original supplied. */ private String getTrimmedAndValidated(final String original, final String type, boolean requireStartWithDash) { // Check sanity Validate.notNull(original, "Cannot handle null " + type + "."); final String trimmed = original.trim(); Validate.notEmpty(trimmed, "Cannot handle empty " + type + "."); if (requireStartWithDash) { Validate.isTrue(trimmed.startsWith("-"), "Expected " + type + " to start with '-', but got: [" + original + "]"); } // All done return trimmed; } /** * Convenience method to add a new argument with the supplied value to the given * Commandline instance. * * @param commandline The command line to which we should add an argument. * @param value the value of the new argument to be added to the Commandline. */ private void createArgWithValue(final Commandline commandline, final String value) { // Check sanity Validate.notEmpty(value, "Cannot handle null or empty value argument."); // All done. commandline.createArg().setValue(value); } }