Java tutorial
package com.chaschev.install; /* * Copyright 2001-2005 The Apache Software Foundation. * * 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. */ import com.chaschev.chutils.util.OpenBean2; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.maven.DefaultMaven; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.artifact.DefaultArtifact; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.eclipse.aether.resolution.DependencyResult; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.text.MessageFormat; import java.util.*; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.transform; import static org.apache.commons.lang3.StringUtils.substringAfter; import static org.apache.commons.lang3.StringUtils.substringBefore; import static org.apache.commons.lang3.SystemUtils.IS_OS_UNIX; @Mojo(name = "install", requiresProject = false, threadSafe = true) public class InstallMojo extends AbstractExecMojo { public static final Function<String, File> PATH_TO_FILE = new Function<String, File>() { public File apply(String s) { return new File(s); } }; public static final class MatchingPath implements Comparable<MatchingPath> { int priority; String path; public MatchingPath(int priority, String path) { this.priority = priority; this.path = path; } @Override public int compareTo(MatchingPath o) { return priority - o.priority; } } @Parameter(property = "installTo") private String installTo; public void execute() throws MojoExecutionException, MojoFailureException { try { // FindAvailableVersions.main(null); initialize(); Artifact artifact = new DefaultArtifact(artifactName); DependencyResult dependencyResult = resolveArtifact(artifact); artifact = dependencyResult.getRoot().getArtifact(); List<ArtifactResult> dependencies = dependencyResult.getArtifactResults(); if (className != null) { new ExecObject(getLog(), artifact, dependencies, className, parseArgs(), systemProperties) .execute(); } Class<?> installation = new URLClassLoader(new URL[] { artifact.getFile().toURI().toURL() }) .loadClass("Installation"); List<Object[]> entries = (List<Object[]>) OpenBean2.getStaticFieldValue(installation, "shortcuts"); if (installTo == null) { installTo = findPath(); } File installToDir = new File(installTo); File classPathFile = writeClasspath(artifact, dependencies, installToDir); for (Object[] entry : entries) { String shortCut = (String) entry[0]; String className = entry[1] instanceof String ? entry[1].toString() : ((Class) entry[1]).getName(); File file; if (SystemUtils.IS_OS_WINDOWS) { file = new File(installToDir, shortCut + ".bat"); FileUtils.writeStringToFile(file, createLaunchScript(className, classPathFile)); } else { file = new File(installToDir, shortCut); FileUtils.writeStringToFile(file, createLaunchScript(className, classPathFile)); try { file.setExecutable(true, false); } catch (Exception e) { getLog().warn( "could not make '" + file.getAbsolutePath() + "' executable: " + e.toString()); } } getLog().info("created a shortcut: " + file.getAbsolutePath() + " -> " + className); } } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { getLog().error(e.toString(), e); throw new MojoExecutionException(e.toString()); } } } private String createLaunchScript(String className, File classPathFile) { String jarPath = getJarByClass(Runner.class).getAbsolutePath(); if (IS_OS_UNIX) { String installerUserHome = getInstallerHomeDir(jarPath); jarPath = jarPath.replace(installerUserHome, "$HOME"); } String appLaunchingString = MessageFormat.format("{0} -cp \"{1}\" {2} {3} {4}", javaExePath(), jarPath, Runner.class.getName(), classPathFile.getAbsolutePath(), className); return sudoInstallationSupportingScript(jarPath, appLaunchingString); } // Solution for sudo installation problem: if you first install the app by sudo mvn installation:install ... // Then plugin which contains the Runner is not on the path of all other users because it has been installed to the root's repo! // To solve this, we first check if the Runner is on the classpath (by running it with a control string). // If it's not, then the plugin dependency is being fetched. // A simpler and may be better option would be to supply a bootstrap (containing the Runner) dependency for the app. // With this solution however there is no need in the dependency and no coupling. private String sudoInstallationSupportingScript(String jarPath, String appLaunchingString) { String script; boolean unix = IS_OS_UNIX; PluginDescriptor desc = (PluginDescriptor) getPluginContext().get("pluginDescriptor"); if (unix) { appLaunchingString = appLaunchingString + " $*"; script = MessageFormat.format( "" + "{0} -cp \"{1}\" {2} SMOKE_TEST_HUH 2> /dev/null\n" + "\n" + "rc=$?\n" + "\n" + "if [[ $rc != 0 ]] ; then\n" + " mvn -U {4}:{5}:{6}:fetch\n" + " {3}\n" + " exit $rc\n" + "fi\n" + "\n" + "{3}\n", javaExePath(), jarPath, Runner.class.getName(), appLaunchingString, desc.getGroupId(), desc.getArtifactId(), desc.getVersion()); } else { //add quotes for "Program Files" case appLaunchingString = '"' + substringBefore(appLaunchingString, " ") + "\" " + substringAfter(appLaunchingString, " "); appLaunchingString = "@" + appLaunchingString + " %*"; script = MessageFormat.format( "" + "@{0} -cp \"{1}\" {2} SMOKE_TEST_HUH 2> nul\n" + "@IF ERRORLEVEL 1 GOTO nok\n" + "{3}\n" + "@goto leave\n\n" + "" + ":nok\n" + "mvn -U {4}:{5}:{6}:fetch\n" + "{3}\n" + "@goto leave\n\n" + "" + ":leave\n", javaExePath(), jarPath, Runner.class.getName(), appLaunchingString, desc.getGroupId(), desc.getArtifactId(), desc.getVersion()); } return script; } private static String getInstallerHomeDir(String jarPath) { return new File(substringBefore(jarPath, "/com/chaschev")).getParentFile().getParentFile() .getAbsolutePath(); } private static File javaExePath() { return new File(SystemUtils.getJavaHome(), "bin/" + (IS_OS_UNIX ? "java" : "java.exe")); } private static File writeClasspath(Artifact artifact, List<ArtifactResult> dependencies, File installToDir) throws IOException { final String jarPath = getJarByClass(Runner.class).getAbsolutePath(); final String installerUserHome = getInstallerHomeDir(jarPath); ArrayList<File> classPathFiles = newArrayList(transform(dependencies, new Function<ArtifactResult, File>() { @Override public File apply(ArtifactResult artifactResult) { return artifactResult.getArtifact().getFile(); } })); classPathFiles.add(getJarByClass(Runner.class)); File file = new File(installToDir, artifact.getGroupId() + "." + artifact.getArtifactId()); FileUtils.writeLines(file, transform(classPathFiles, new Function<File, String>() { @Override public String apply(File file) { if (IS_OS_UNIX) { return file.getAbsolutePath().replace(installerUserHome, "$HOME"); } else { return file.getAbsolutePath(); } } })); return file; } private String findPath() throws MojoFailureException { String path = Optional.fromNullable(System.getenv("path")).or(System.getenv("PATH")); ArrayList<String> pathEntries = newArrayList(path == null ? new String[0] : path.split(File.pathSeparator)); String javaHomeAbsPath = SystemUtils.getJavaHome().getParentFile().getAbsolutePath(); String mavenHomeAbsPath = getMavenHomeByClass(DefaultMaven.class).getAbsolutePath(); List<MatchingPath> matchingPaths = new ArrayList<MatchingPath>(); final LinkedHashSet<File> knownBinFolders = Sets.newLinkedHashSet( Lists.transform(Arrays.asList("/usr/local/bin", "/usr/local/sbin"), PATH_TO_FILE)); for (String pathEntry : pathEntries) { File entryFile = new File(pathEntry); String absPath = entryFile.getAbsolutePath(); boolean writable = isWritable(entryFile); getLog().debug( "testing " + entryFile.getAbsolutePath() + ": " + (writable ? "writable" : "not writable")); if (absPath.startsWith(javaHomeAbsPath)) { addMatching(matchingPaths, absPath, writable, 1); } else if (absPath.startsWith(mavenHomeAbsPath)) { addMatching(matchingPaths, absPath, writable, 2); } } if (IS_OS_UNIX && matchingPaths.isEmpty()) { getLog().warn("didn't find maven/jdk writable roots available on path, trying common unix paths: " + knownBinFolders); final LinkedHashSet<File> pathEntriesSet = Sets .newLinkedHashSet(Lists.transform(pathEntries, PATH_TO_FILE)); for (File knownBinFolder : knownBinFolders) { if (pathEntriesSet.contains(knownBinFolder)) { addMatching(matchingPaths, knownBinFolder.getAbsolutePath(), isWritable(knownBinFolder), 3); } } } Collections.sort(matchingPaths); if (matchingPaths.isEmpty()) { throw new MojoFailureException("Could not find a bin folder to write to. Tried: \n" + Joiner.on("\n").join(mavenHomeAbsPath, javaHomeAbsPath) + "\n" + (IS_OS_UNIX ? knownBinFolders + "\n" : "") + " but they don't appear on the path or are not writable. You may try running as administrator or specifying -DinstallTo=your-bin-dir-path parameter"); } return matchingPaths.get(0).path; } private void addMatching(List<MatchingPath> matchingPaths, String matchingPath, boolean writable, int type) { if (writable) { getLog().info(matchingPath + " matches"); matchingPaths.add(new MatchingPath(type, matchingPath)); } else { getLog().warn(matchingPath + " matches, but is not writable"); } } private static File getMavenHomeByClass(Class<?> aClass) { return getJarByClass(aClass).getParentFile().getParentFile(); } public static File getJarByClass(Class<?> aClass) { return new File(aClass.getProtectionDomain().getCodeSource().getLocation().getFile()); } private static boolean isWritable(File dir) { try { File tempFile = File.createTempFile("testWrite", "txt", dir); if (tempFile.exists()) { tempFile.delete(); return true; } } catch (IOException e) { return false; } return dir.canWrite(); } }