All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scijava.util.AppUtils Maven / Gradle / Ivy

/*
 * #%L
 * SciJava Common shared library for SciJava software.
 * %%
 * Copyright (C) 2009 - 2017 Board of Regents of the University of
 * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
 * Institute of Molecular Cell Biology and Genetics, University of
 * Konstanz, and KNIME GmbH.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.util;

import java.io.File;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.scijava.util.Types;

/**
 * Useful methods for obtaining details of the SciJava application environment.
 * 
 * @author Johannes Schindelin
 * @author Curtis Rueden
 */
public final class AppUtils {

	private static final Class mainClass;

	static {
		// Get the class whose main method launched the application. The heuristic
		// will fail if the main thread has terminated before this class loads.
		final String className = DebugUtils.getMainClassName();
		mainClass = className == null ? null : Types.load(className);
	}

	private AppUtils() {
		// prevent instantiation of utility class
	}

	/**
	 * Gets the class whose main method launched the application.
	 * 
	 * @return The launching class, or null if the main method terminated before
	 *         the {@code AppUtils} class was loaded.
	 */
	public static Class getMainClass() {
		return mainClass;
	}

	/**
	 * Gets the application root directory. If the given system property is set,
	 * it is used. Otherwise, we scan up the tree from the given class for a
	 * suitable directory.
	 * 
	 * @param sysProp System property which may point at the root directory. If
	 *          this is set to a valid directory, it is used.
	 * @param c The class from which the base directory should be derived.
	 * @param baseSubdirectory A hint for what to expect for a directory structure
	 *          beneath the application base directory. If this value is null
	 *          (i.e., no hint is given), the heuristic scans up the directory
	 *          tree looking for the topmost pom.xml file.
	 * @see AppUtils#getBaseDirectory(File, String)
	 */
	public static File getBaseDirectory(final String sysProp, final Class c,
		final String baseSubdirectory)
	{
		final String property = System.getProperty(sysProp);
		if (property != null) {
			final File dir = new File(property);
			if (dir.isDirectory()) return dir;
		}

		// look for valid base directory relative to this class
		final File basePath = AppUtils.getBaseDirectory(c, baseSubdirectory);
		if (basePath != null) return basePath;

		// NB: Look for valid base directory relative to the main class which
		// launched this environment. We will reach this logic, e.g., if the
		// application is running via "mvn exec:exec". In this case, most classes
		// (including this one) will be located in JARs in the local Maven
		// repository cache (~/.m2/repository), so the corePath will be null.
		// However, the classes of the launching project will be located in
		// target/classes, so we search up the tree from one of those.
		final File appPath = AppUtils.getBaseDirectory(AppUtils.getMainClass());
		if (appPath != null) return appPath;

		// last resort: use current working directory
		return new File(".").getAbsoluteFile();
	}

	/**
	 * Gets the base file system directory containing the given class file.
	 * 
	 * @param c The class from which the base directory should be derived.
	 * @see #getBaseDirectory(File, String)
	 */
	public static File getBaseDirectory(final Class c) {
		return getBaseDirectory(c, null);
	}

	/**
	 * Gets the base file system directory containing the given class file.
	 * 
	 * @param c The class from which the base directory should be derived.
	 * @param baseSubdirectory A hint for what to expect for a directory structure
	 *          beneath the application base directory.
	 * @see #getBaseDirectory(File, String)
	 */
	public static File getBaseDirectory(final Class c,
		final String baseSubdirectory)
	{
		// see: http://stackoverflow.com/a/12733172/1207769

		// step 1: convert Class to URL
		final URL location = Types.location(c);

		// step 2: convert URL to File
		File baseFile;
		try {
			baseFile = FileUtils.urlToFile(location);
		}
		catch (final IllegalArgumentException exc) {
			// URL can't be converted to a file
			baseFile = null;
		}

		// step 3: get the file's base directory
		return getBaseDirectory(baseFile, baseSubdirectory);
	}

	/**
	 * Gets the base file system directory from the given known class location.
	 * 

* This method uses heuristics to find the base directory in a variety of * situations. Depending on your execution environment, the class may be * located in one of several different places: *

*
    *
  1. In the Maven build directory. Typically this is the * {@code target/classes} folder of a given component. In this case, the class * files reside directly on the file system (not in a JAR file). The base * directory is defined as the toplevel Maven directory for the multi-module * project. For example, if you have checked out {@code scijava-common.git} to * {@code ~/sjc}, the {@code org.scijava.Context} class will be located at * {@code ~/sjc/scijava-common/target/classes/org/scijava/Context.class}. * Asking for the base directory for that class will yield * {@code ~/sjc/scijava-common}, as long as you correctly specify * {@code scijava-common} for the {@code baseSubdirectory}.
  2. *
  3. Within a JAR file in the Maven local repository cache. Typically * this cache is located in {@code ~/.m2/repository}. The location will be * {@code groupId/artifactId/version/artifactId-version.jar} where * {@code groupId}, {@code artifactId} and {@code version} are the Maven GAV * coordinates. Note that in this case, no base directory with respect to * the given class can be found, and this method will return null.
  4. *
  5. Within a JAR file beneath the base directory. Common cases * include running the application from a standalone application bundle (e.g., * the JARs may all be located within a {@code jars} folder of the application * distribution archive) or using a JAR packaged by Maven and residing in the * Maven build folder (typically {@code target/artifactId-version.jar}). * However, this could potentially be anywhere beneath the base directory. * This method assumes the JAR will be nested exactly one level deep; i.e., it * computes the base directory as the parent directory of the one containing * the JAR file.
  6. *
*

* As you can see, it is quite complicated to find the base directory properly * in all cases. The specific execution environments we have tested include: *

*
    *
  1. Running from Eclipse. How Eclipse structures the classpath * depends on which dependencies are open (i.e., project dependencies) versus * closed (i.e., JAR dependencies). Each project dependency has a classpath * entry in its Maven build directory. Each JAR dependency has a * classpath entry within a JAR file in the Maven local repository * cache.
  2. *
  3. Running via Maven. If you execute the application using Maven * (e.g., {@code mvn exec:exec}, or with a fully Maven-compatible IDE such as * NetBeans or IntelliJ IDEA) then all dependencies will reside within JAR * files in the local Maven repository cache. But the executed project * itself will reside in its Maven build directory. So as long as you * ask for the base directory relative to a class * of the executed project it will be found.
  4. *
  5. Running as an application bundle (e.g., ImageJ). Typically this * means downloading ImageJ from the web site, unpacking it and running the * ImageJ launcher (double-clicking ImageJ-win32.exe on Windows, * double-clicking the {@code ImageJ.app} on OS X, etc.). In this case, all * components reside in the {@code jars} folder of the application bundle, and * the base directory will be found one level above that.
  6. *
* * @param classLocation The location from which the base directory should be * derived. * @param baseSubdirectory A hint for what to expect for a directory structure * beneath the application base directory. If this value is null * (i.e., no hint is given), the heuristic scans up the directory * tree looking for the topmost pom.xml file. */ public static File getBaseDirectory(final File classLocation, final String baseSubdirectory) { if (classLocation == null) return null; String path = FileUtils.getPath(classLocation).replace('\\', '/'); if (path.contains("/.m2/repository/")) { // NB: The class is in a JAR in the Maven repository cache. // We cannot find the application base directory relative to this path. return null; } // check whether the class is in a Maven build directory String basePrefix = "/"; if (baseSubdirectory != null) basePrefix += baseSubdirectory + "/"; final String targetClassesSuffix = basePrefix + "target/classes"; final String targetTestClassesSuffix = basePrefix + "target/test-classes"; final String[] suffixes = {targetClassesSuffix, targetTestClassesSuffix}; for (final String suffix : suffixes) { if (!path.endsWith(suffix)) continue; // NB: The class is a file beneath a Maven build directory. path = path.substring(0, path.length() - suffix.length()); File dir = new File(path); if (baseSubdirectory == null) { // NB: There is no hint as to the directory structure. // So we scan up the tree to find the topmost pom.xml file. while (dir.getParentFile() != null && new File(dir.getParentFile(), "pom.xml").exists()) { dir = dir.getParentFile(); } } return dir; } final Pattern pattern = Pattern.compile(".*(" + Pattern.quote(basePrefix + "target/") + "[^/]*\\.jar)"); final Matcher matcher = pattern.matcher(path); if (matcher.matches()) { // NB: The class is in the Maven build directory inside a JAR file // ("target/[something].jar"). final int index = matcher.start(1); path = path.substring(0, index); return new File(path); } if (path.endsWith(".jar")) { final File jarDirectory = classLocation.getParentFile(); // NB: The class is in a JAR file, which we assume is nested one level // deep (i.e., directly beneath the application base directory). return jarDirectory.getParentFile(); } // NB: As a last resort, we use the class location directly. return classLocation; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy