Java tutorial
/** * Pyx4me framework * Copyright (C) 2006-2008 pyx4j.com. * * 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. * * @author vlads * @version $Id: ProGuardMojo.java 10671 2013-09-25 08:18:35Z thedoux $ */ package com.all4tec.sa.maven.proguard; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.maven.archiver.MavenArchiveConfiguration; import org.apache.maven.archiver.MavenArchiver; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Dependency; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Java; import org.codehaus.plexus.archiver.jar.JarArchiver; /** * * <p> * The Obfuscate task provides a stand-alone obfuscation task * </p> * * @goal proguard * @phase package * @description Create small jar files using ProGuard * @requiresDependencyResolution compile * @threadSafe */ public class ProGuardMojo extends AbstractMojo { /** * Set this to 'true' to bypass ProGuard processing entirely. * * @parameter expression="${proguard.skip}" */ private boolean skip; /** * Set this to <code>true</code> to get args for Proguard jar wrote in a file rather than in a command line * * @parameter * default-value="true" */ private boolean writeCommandLineToFile; /** * Recursively reads configuration options from the given file filename * * @parameter default-value="${basedir}/proguard.conf" */ private File proguardInclude; /** * Select specific ProGuard version from plugin dependencies * * @parameter */ private String proguardVersion; /** * ProGuard configuration options * * @parameter */ private String[] options; /** * Specifies not to obfuscate the input class files. * * @parameter default-value="true" */ private boolean obfuscate; /** * Specifies that project compile dependencies be added as -libraryjars to proguard arguments. Dependency itself is * not included in resulting jar * * @parameter default-value="true" */ private boolean includeDependency; /** * Bundle project dependency to resulting jar. Specifies list of artifact inclusions * * @parameter */ private Assembly assembly; /** * Additional -libraryjars e.g. ${java.home}/lib/rt.jar Project compile dependency are added automatically. See * exclusions * * @parameter */ private List libs; /** * List of dependency exclusions * * @parameter */ private List exclusions; /** * Specifies the input jar name (or wars, ears, zips) of the application to be * processed. * * You may specify a classes directory e.g. 'classes'. This way plugin will processed * the classes instead of jar. You would need to bind the execution to phase 'compile' * or 'process-classes' in this case. * * @parameter expression="${project.build.finalName}.jar" * @required */ protected String injar; /** * Set this to 'true' to bypass ProGuard processing when injar does not exists. * * @parameter default-value="false" */ private boolean injarNotExistsSkip; /** * Apply ProGuard classpathentry Filters to input jar. e.g. <code>!**.gif,!**/tests/**'</code> * * @parameter */ protected String inFilter; /** * Specifies the names of the output jars. If attach=true the value ignored and name constructed base on classifier * If empty input jar would be overdriven. * * @parameter */ protected String outjar; /** * Specifies whether or not to attach the created artifact to the project * * @parameter default-value="false" */ private boolean attach = false; /** * Specifies attach artifact type * * @parameter default-value="jar" */ private String attachArtifactType; /** * Specifies attach artifact Classifier, Ignored if attach=false * * @parameter default-value="small" */ private String attachArtifactClassifier; /** * Set to false to exclude the attachArtifactClassifier from the Artifact final name. Default value is true. * * @parameter default-value="true" */ private boolean appendClassifier; /** * Set to true to include META-INF/maven/** maven descriptord * * @parameter default-value="false" */ private boolean addMavenDescriptor; /** * Directory containing the input and generated JAR. * * @parameter expression="${project.build.directory}" * @required */ protected File outputDirectory; /** * The Maven project reference where the plugin is currently being executed. The default value is populated from * maven. * * @parameter expression="${project}" * @readonly * @required */ protected MavenProject mavenProject; /** * The plugin dependencies. * * @parameter expression="${plugin.artifacts}" * @required * @readonly */ protected List pluginArtifacts; /** * @component */ private MavenProjectHelper projectHelper; /** * The Jar archiver. * * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar" * @required */ private JarArchiver jarArchiver; /** * The maven archive configuration to use. only if assembly is used. * * @parameter */ protected MavenArchiveConfiguration archive = new MavenArchiveConfiguration(); /** * The max memory the forked java process should use, e.g. 256m * * @parameter */ protected String maxMemory; /** * ProGuard main class name. * * @parameter default-value="proguard.ProGuard" */ protected String proguardMainClass = "proguard.ProGuard"; private Log log; /** * ProGuard docs: Names with special characters like spaces and parentheses must be quoted with single or double * quotes. */ private static String fileNameToString(String fileName) { return "'" + fileName + "'"; } private static String fileToString(File file) { return fileNameToString(file.toString()); } private boolean useArtifactClassifier() { return appendClassifier && ((attachArtifactClassifier != null) && (attachArtifactClassifier.length() > 0)); } public void execute() throws MojoExecutionException, MojoFailureException { log = getLog(); if (skip) { log.info("Bypass ProGuard processing because \"proguard.skip=true\""); return; } boolean mainIsJar = mavenProject.getPackaging().equals("jar"); boolean mainIsPom = mavenProject.getPackaging().equals("pom"); File inJarFile = new File(outputDirectory, injar); if (mainIsJar && (!inJarFile.exists())) { if (injarNotExistsSkip) { log.info("Bypass ProGuard processing because \"injar\" dos not exist"); return; } throw new MojoFailureException("Can't find file " + inJarFile); } if (mainIsPom && (!inJarFile.exists()) && injarNotExistsSkip) { log.info("Bypass ProGuard processing because \"injar\" dos not exist"); return; } if (!outputDirectory.exists()) { if (!outputDirectory.mkdirs()) { throw new MojoFailureException("Can't create " + outputDirectory); } } File outJarFile; boolean sameArtifact; if (attach) { outjar = nameNoType(injar); if (useArtifactClassifier()) { outjar += "-" + attachArtifactClassifier; } outjar += "." + attachArtifactType; } if ((outjar != null) && (!outjar.equals(injar))) { sameArtifact = false; outJarFile = (new File(outputDirectory, outjar)).getAbsoluteFile(); if (outJarFile.exists()) { if (!deleteFileOrDirectory(outJarFile)) { throw new MojoFailureException("Can't delete " + outJarFile); } } } else { sameArtifact = true; outJarFile = inJarFile.getAbsoluteFile(); File baseFile; if (inJarFile.isDirectory()) { baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_base"); } else { baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_base.jar"); } if (baseFile.exists()) { if (!deleteFileOrDirectory(baseFile)) { throw new MojoFailureException("Can't delete " + baseFile); } } if (inJarFile.exists()) { if (!inJarFile.renameTo(baseFile)) { throw new MojoFailureException("Can't rename " + inJarFile); } } inJarFile = baseFile; } ArrayList<String> args = new ArrayList<String>(); if (log.isDebugEnabled()) { List dependancy = mavenProject.getCompileArtifacts(); for (Iterator i = dependancy.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); log.debug("--- compile artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope()); } for (Iterator i = mavenProject.getArtifacts().iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); log.debug("--- artifact " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope()); } for (Iterator i = mavenProject.getDependencies().iterator(); i.hasNext();) { Dependency artifact = (Dependency) i.next(); log.debug("--- dependency " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType() + ":" + artifact.getClassifier() + " Scope:" + artifact.getScope()); } } Set inPath = new HashSet(); boolean hasInclusionLibrary = false; if (assembly != null) { for (Iterator iter = assembly.inclusions.iterator(); iter.hasNext();) { Inclusion inc = (Inclusion) iter.next(); if (!inc.library) { File file = getClasspathElement(getDependancy(inc, mavenProject), mavenProject); inPath.add(file.toString()); log.debug("--- ADD injars:" + inc.artifactId); StringBuffer filter = new StringBuffer(fileToString(file)); filter.append("(!META-INF/MANIFEST.MF"); if (!addMavenDescriptor) { filter.append(","); filter.append("!META-INF/maven/**"); } if (inc.filter != null) { filter.append(",").append(inc.filter); } filter.append(")"); args.add("-injars"); args.add(filter.toString()); } else { hasInclusionLibrary = true; log.debug("--- ADD libraryjars:" + inc.artifactId); // This may not be CompileArtifacts, maven 2.0.6 bug File file = getClasspathElement(getDependancy(inc, mavenProject), mavenProject); inPath.add(file.toString()); args.add("-libraryjars"); args.add(fileToString(file)); } } } if ((!mainIsPom) && inJarFile.exists()) { args.add("-injars"); StringBuffer filter = new StringBuffer(fileToString(inJarFile)); if ((inFilter != null) || (!addMavenDescriptor)) { filter.append("("); boolean coma = false; if (!addMavenDescriptor) { coma = true; filter.append("!META-INF/maven/**"); } if (inFilter != null) { if (coma) { filter.append(","); } filter.append(inFilter); } filter.append(")"); } args.add(filter.toString()); } args.add("-outjars"); args.add(fileToString(outJarFile)); if (!obfuscate) { args.add("-dontobfuscate"); } if (proguardInclude != null) { if (proguardInclude.exists()) { args.add("-include"); args.add(fileToString(proguardInclude)); log.debug("proguardInclude " + proguardInclude); } else { log.debug("proguardInclude config does not exists " + proguardInclude); } } if (includeDependency) { List dependency = this.mavenProject.getCompileArtifacts(); for (Iterator i = dependency.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); // dependency filter if (isExclusion(artifact)) { continue; } File file = getClasspathElement(artifact, mavenProject); if (inPath.contains(file.toString())) { log.debug("--- ignore libraryjars since one in injar:" + artifact.getArtifactId()); continue; } log.debug("--- ADD libraryjars:" + artifact.getArtifactId()); args.add("-libraryjars"); args.add(fileToString(file)); } } if (libs != null) { for (Iterator i = libs.iterator(); i.hasNext();) { Object lib = i.next(); args.add("-libraryjars"); args.add(fileNameToString(lib.toString())); } } args.add("-printmapping"); args.add(fileToString((new File(outputDirectory, "proguard_map.txt").getAbsoluteFile()))); args.add("-printseeds"); args.add(fileToString((new File(outputDirectory, "proguard_seeds.txt").getAbsoluteFile()))); if (log.isDebugEnabled()) { args.add("-verbose"); } if (options != null) { for (int i = 0; i < options.length; i++) { args.add(options[i]); } } // Check if args should be inlined in a proguard configuration file or not. If args total size is more than 32k, // process launch will failed File vTempFile = null; if (writeCommandLineToFile) { log.info("Transform command line in file configuration"); vTempFile = createFileConfiguration(args, mavenProject, outputDirectory); // Remove all args, and add just path to Proguard configuration file just created args.clear(); args.add("@" + vTempFile.getAbsolutePath()); log.info("Configuration file created : " + vTempFile.getAbsolutePath()); } log.info("execute ProGuard " + args.toString()); proguardMain(getProguardJar(this), args, this); if ((assembly != null) && (hasInclusionLibrary)) { log.info("creating assembly"); File baseFile = new File(outputDirectory, nameNoType(injar) + "_proguard_result.jar"); if (baseFile.exists()) { if (!baseFile.delete()) { throw new MojoFailureException("Can't delete " + baseFile); } } File archiverFile = outJarFile.getAbsoluteFile(); if (!outJarFile.renameTo(baseFile)) { throw new MojoFailureException("Can't rename " + outJarFile); } MavenArchiver archiver = new MavenArchiver(); archiver.setArchiver(jarArchiver); archiver.setOutputFile(archiverFile); archive.setAddMavenDescriptor(addMavenDescriptor); try { jarArchiver.addArchivedFileSet(baseFile); for (Iterator iter = assembly.inclusions.iterator(); iter.hasNext();) { Inclusion inc = (Inclusion) iter.next(); if (inc.library) { File file; Artifact artifact = getDependancy(inc, mavenProject); file = getClasspathElement(artifact, mavenProject); if (file.isDirectory()) { getLog().info("merge project: " + artifact.getArtifactId() + " " + file); jarArchiver.addDirectory(file); } else { getLog().info("merge artifact: " + artifact.getArtifactId()); jarArchiver.addArchivedFileSet(file); } } } archiver.createArchive(mavenProject, archive); } catch (Exception e) { throw new MojoExecutionException("Unable to create jar", e); } } if (attach && !sameArtifact) { if (useArtifactClassifier()) { projectHelper.attachArtifact(mavenProject, attachArtifactType, attachArtifactClassifier, outJarFile); } else { projectHelper.attachArtifact(mavenProject, attachArtifactType, null, outJarFile); } } } private static File getProguardJar(ProGuardMojo mojo) throws MojoExecutionException { Artifact proguardArtifact = null; int proguardArtifactDistance = -1; // This should be solved in Maven 2.1 for (Iterator i = mojo.pluginArtifacts.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); mojo.getLog().debug("pluginArtifact: " + artifact.getFile()); if (artifact.getArtifactId().startsWith("proguard") && !artifact.getArtifactId().startsWith("proguard-maven-plugin")) { int distance = artifact.getDependencyTrail().size(); mojo.getLog().debug("proguard DependencyTrail: " + distance); if ((mojo.proguardVersion != null) && (mojo.proguardVersion.equals(artifact.getVersion()))) { proguardArtifact = artifact; break; } else if (proguardArtifactDistance == -1) { proguardArtifact = artifact; proguardArtifactDistance = distance; } else if (distance < proguardArtifactDistance) { proguardArtifact = artifact; proguardArtifactDistance = distance; } } } if (proguardArtifact != null) { mojo.getLog().debug("proguardArtifact: " + proguardArtifact.getFile()); return proguardArtifact.getFile().getAbsoluteFile(); } mojo.getLog().info("proguard jar not found in pluginArtifacts"); ClassLoader cl; cl = mojo.getClass().getClassLoader(); // cl = Thread.currentThread().getContextClassLoader(); String classResource = "/" + mojo.proguardMainClass.replace('.', '/') + ".class"; URL url = cl.getResource(classResource); if (url == null) { throw new MojoExecutionException( "Obfuscation failed ProGuard (" + mojo.proguardMainClass + ") not found in classpath"); } String proguardJar = url.toExternalForm(); if (proguardJar.startsWith("jar:file:")) { proguardJar = proguardJar.substring("jar:file:".length()); proguardJar = proguardJar.substring(0, proguardJar.indexOf('!')); } else { throw new MojoExecutionException("Unrecognized location (" + proguardJar + ") in classpath"); } return new File(proguardJar); } private static void proguardMain(File proguardJar, ArrayList<String> argsList, ProGuardMojo mojo) throws MojoExecutionException { Java java = new Java(); Project antProject = new Project(); antProject.setName(mojo.mavenProject.getName()); antProject.init(); DefaultLogger antLogger = new DefaultLogger(); antLogger.setOutputPrintStream(System.out); antLogger.setErrorPrintStream(System.err); antLogger.setMessageOutputLevel(mojo.log.isDebugEnabled() ? Project.MSG_DEBUG : Project.MSG_INFO); antProject.addBuildListener(antLogger); antProject.setBaseDir(mojo.mavenProject.getBasedir()); java.setProject(antProject); java.setTaskName("proguard"); mojo.getLog().info("proguard jar: " + proguardJar); java.createClasspath().setLocation(proguardJar); // java.createClasspath().setPath(System.getProperty("java.class.path")); java.setClassname(mojo.proguardMainClass); java.setFailonerror(true); java.setFork(true); // get the maxMemory setting if (mojo.maxMemory != null) { java.setMaxmemory(mojo.maxMemory); } for (Iterator<String> i = argsList.iterator(); i.hasNext();) { java.createArg().setValue(i.next()); } int result = java.executeJava(); if (result != 0) { throw new MojoExecutionException("Obfuscation failed (result=" + result + ")"); } } private static String nameNoType(String fileName) { int extStart = fileName.lastIndexOf('.'); if (extStart == -1) { return fileName; } return fileName.substring(0, extStart); } private static boolean deleteFileOrDirectory(File path) throws MojoFailureException { if (path.isDirectory()) { File[] files = path.listFiles(); for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { if (!deleteFileOrDirectory(files[i])) { throw new MojoFailureException("Can't delete dir " + files[i]); } } else { if (!files[i].delete()) { throw new MojoFailureException("Can't delete file " + files[i]); } } } return path.delete(); } else { return path.delete(); } } private static Artifact getDependancy(Inclusion inc, MavenProject mavenProject) throws MojoExecutionException { Set dependancy = mavenProject.getArtifacts(); for (Iterator i = dependancy.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); if (inc.match(artifact)) { return artifact; } } throw new MojoExecutionException("artifactId Not found " + inc.artifactId); } private boolean isExclusion(Artifact artifact) { if (exclusions == null) { return false; } for (Iterator iter = exclusions.iterator(); iter.hasNext();) { Exclusion excl = (Exclusion) iter.next(); if (excl.match(artifact)) { return true; } } return false; } private static File getClasspathElement(Artifact artifact, MavenProject mavenProject) throws MojoExecutionException { if (artifact.getClassifier() != null) { return artifact.getFile(); } String refId = artifact.getGroupId() + ":" + artifact.getArtifactId(); MavenProject project = (MavenProject) mavenProject.getProjectReferences().get(refId); if (project != null) { return new File(project.getBuild().getOutputDirectory()); } else { File file = artifact.getFile(); if ((file == null) || (!file.exists())) { throw new MojoExecutionException("Dependency Resolution Required " + artifact); } return file; } } /** * Create a temporary file and fill it with Proguard configuration. * * @param pArgsList List of args to push in Proguard configuration file * @param pMavenProject current project * @exception MojoExecutionException Exception throw if problem occurs on file creation */ private static File createFileConfiguration(List<String> pArgsList, MavenProject pMavenProject, File pOutputDirectory) throws MojoExecutionException { File vTempFile = null; try { String vFileName = pMavenProject.getName() + "arguments-inlining.proguardconf"; vTempFile = new File(pOutputDirectory.getAbsolutePath(), vFileName); Writer vWriter = new BufferedWriter(new FileWriter(vTempFile)); // Get iterator Iterator<String> vArgsIterator = pArgsList.iterator(); // Iterate and add each line with a space behind while (vArgsIterator.hasNext()) { String vNextArg = vArgsIterator.next(); vWriter.write(vNextArg + " "); } IOUtils.closeQuietly(vWriter); } catch (IOException pException) { throw new MojoExecutionException("Unable to create proguard configuration file ", pException); } return vTempFile; } }