/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Helper class to get platform information.
*
* @author Nils Woehler
*/
@SuppressWarnings({ "PMD.AvoidSynchronizedAtMethodLevel", "PMD.TooManyMethods", "PMD.GodClass" })
public final class PlatformUtilities {
private static final String PLATFORM_PROPERTIES = "conf/platform.properties";
private static final String PLATFORM_PROPERTY_KEY = "platform";
private static final String GRADLE_PROPERTIES = "gradle.properties";
private static final String VERSION_PROPERTY_KEY = "version";
private static final String REVISION_PROPERTY_KEY = "revision";
/**
* The name of the RapidMiner Studio Launcher Jar
*/
private static final String RAPIDMINER_STUDIO_LAUNCHER_JAR = "rapidminer-studio-launcher.jar";
/**
* The name of the RapidMiner Studio Commons development folder
*/
private static final String RAPIDMINER_STUDIO_COMMONS = "rapidminer-studio-commons";
/**
* A regular expression that matches any version of the RapidMiner Studio Core Jar
*/
private static final String RAPIDMINER_STUDIO_CORE_REGEX = ".+rapidminer-studio-core-\\d.*\\.jar";
/**
* The name of the RapidMiner Studio Core development folder
*/
private static final String RAPIDMINER_STUDIO_CORE = "rapidminer-studio-core";
/**
* The suffix of a path from where the tests are started via Gradle.<br/>
* (Gradle copies created jars to '$PROJECT/target/libs')
*/
private static final String TEST_ENVIRONMENT_SUFFIX = File.separatorChar + "target" + File.separatorChar + "libs";
private static final Logger LOGGER = Logger.getLogger(PlatformUtilities.class.getSimpleName());
/** The name of the property indicating the home directory of RapidMiner. */
public static final String PROPERTY_RAPIDMINER_HOME = "rapidminer.home";
/**
* All available release platforms.
*/
public enum Platform {
/**
* Platform independent release
*/
ANY,
/**
* Windows 32-bit
*/
WIN32,
/**
* Windows 64-bit
*/
WIN64,
/**
* Apple OS X
*/
OSX
}
/**
* Cache for current release platform.
*/
private static Platform currentPlatform = null;
/**
* Cache for version of current release.
*/
private static String currentVersion = null;
/**
* Cache for revision of current release.
*/
private static String currentRevision = null;
private static boolean isInitialized = false;
private static final Object INIT_VERSION_LOCK = new Object();
/**
* Utility class constructor.
*/
private PlatformUtilities() {
throw new AssertionError();
}
/**
* Initializes the PlatformUtilities caches
*/
public static synchronized void initialize() {
if (!isInitialized) {
// silently ensure that the RapidMiner Home system property is set
ensureRapidMinerHomeSet(Level.OFF);
initializeReleasePlatform();
initializeReleaseVersion();
initializeReleaseRevision();
isInitialized = true;
}
}
/**
* @return the platform the RapidMiner Studio release was built for. Will return
* <code>null</code> in case platform was not defined.
*/
public static synchronized Platform getReleasePlatform() {
if (!isInitialized) {
initialize();
}
return currentPlatform;
}
/**
* Loads release platform from properties file.
*/
private static void initializeReleasePlatform() {
String platformProperty = readConfigProperty(PLATFORM_PROPERTIES, PLATFORM_PROPERTY_KEY);
if (platformProperty == null) {
logInfo("Release platform not defined.");
} else {
currentPlatform = Platform.valueOf(platformProperty.toUpperCase(Locale.UK));
logInfo("Release platform: " + currentPlatform);
}
}
/**
* @return the current version of the platform release as {@link String}.
*/
public static synchronized String getReleaseVersion() {
if (!isInitialized) {
initialize();
}
return currentVersion;
}
/**
* Initializes the current version by reading the version.properties file
*/
private static void initializeReleaseVersion() {
synchronized (INIT_VERSION_LOCK) {
currentVersion = readResourceProperty(VERSION_PROPERTY_KEY);
if (currentVersion == null) {
logInfo("Could not read current version from resources. Looking for 'gradle.properties'...");
currentVersion = readConfigProperty(GRADLE_PROPERTIES, VERSION_PROPERTY_KEY);
if (currentVersion == null) {
throw new IllegalStateException("Could not initialize RapidMiner Studio version from properties file");
}
}
}
}
/**
* @return the current version of the platform release as {@link String}.
*/
public static synchronized String getReleaseRevision() {
if (!isInitialized) {
initialize();
}
return currentRevision;
}
/**
* Initializes the current version by reading the version.properties file
*/
private static void initializeReleaseRevision() {
synchronized (INIT_VERSION_LOCK) {
currentRevision = readResourceProperty(REVISION_PROPERTY_KEY);
if (currentRevision == null) {
logInfo("Could not read current revision from resources.");
}
}
}
/**
* @return the system property 'rapidminer.home'.
*/
public static synchronized String getRapidMinerHome() {
return System.getProperty(PROPERTY_RAPIDMINER_HOME);
}
/**
* Reads a value from a .properties file stored in the resources of this JAR.
*/
private static String readResourceProperty(String propertyKey) {
Properties props = new Properties();
try (InputStream resourceAsStream = PlatformUtilities.class.getResourceAsStream("version.properties")) {
if (resourceAsStream == null) {
logInfo("Version resource file not found at 'com/rapidminer/tools/version.properties'.");
return null;
} else {
props.load(resourceAsStream);
}
} catch (IOException e) {
logWarn("Error reading version properties from resources file!" + e.getLocalizedMessage());
}
return props.getProperty(propertyKey);
}
/**
* Reads a value from a .properties file stored relative to the folder specified by the system
* property 'rapidminer.home'.
*/
private static String readConfigProperty(String relativePath, String propertyKey) {
String home = getRapidMinerHome();
if (home == null) {
logWarn("Property 'rapidminer.home' not set. Cannot read property file '" + relativePath + "'");
return null;
}
File propertyFile = new File(home, relativePath);
if (propertyFile.canRead()) {
Properties props = new Properties();
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(propertyFile), StandardCharsets.UTF_8)) {
props.load(reader);
} catch (IOException e) {
logWarn("Error reading properties file! " + e.getLocalizedMessage());
}
return props.getProperty(propertyKey);
} else {
logWarn("Property file (" + propertyFile + ") not found or not readable!");
return null;
}
}
/**
* Ensures that the environment variable 'rapidminer.home' is set by calling
* {@link #ensureRapidMinerHomeSet(Level)} with {@link Level#INFO}.
*/
public static void ensureRapidMinerHomeSet() {
ensureRapidMinerHomeSet(Level.INFO);
}
/**
* Ensures that the environment variable 'rapidminer.home' is set by searching for RapidMiner
* Jars in classpath and build dir.
*
* @param logLevel
* the {@link Level} to log method informations
*/
public static synchronized void ensureRapidMinerHomeSet(final Level logLevel) {
LOGGER.setLevel(logLevel);
if (getRapidMinerHome() == null) {
logInfo("Property " + PROPERTY_RAPIDMINER_HOME + " is not set. Guessing.");
if (!searchInClassPath()) {
try {
logInfo("Property " + PROPERTY_RAPIDMINER_HOME
+ " not found via search in Classpath. Searching in build directory.");
searchInBuildDir();
} catch (Throwable e) { // NOPMD
// important: not only URI Syntax Exception since the
// program must not crash
// in any case!!!
// For example: RapidNet integration as applet into
// Siebel would cause
// problem with new File(...)
logSevere("Failed to locate 'rapidminer.home'! Cause: " + e.getLocalizedMessage());
}
}
} else {
logInfo(PROPERTY_RAPIDMINER_HOME + " is '" + getRapidMinerHome() + "'.");
}
}
/**
* Checks the Java Classpath for rapidminer studio launcher or core jars.
*
* @return
*/
private static boolean searchInClassPath() {
logInfo("Searching in Java classpath for RapidMiner Studio jars...");
String classpath = System.getProperty("java.class.path");
String[] pathComponents = classpath.split(File.pathSeparator);
for (int i = 0; i < pathComponents.length; i++) {
String path = pathComponents[i].trim();
/*
* Search for either 'rapidminer-studio-launcher.jar' or for
* 'rapidminer-studio-core-x.x.xxx.jar'
*/
if (path.matches(RAPIDMINER_STUDIO_CORE_REGEX) || path.endsWith(RAPIDMINER_STUDIO_LAUNCHER_JAR)) {
File jar = new File(path).getAbsoluteFile();
logInfo("Trying parent directory of '" + jar + "'...");
// Retrieve the directory the jar is placed in
File dir = jar.getParentFile();
if (dir == null) {
logSevere("Failed to retrieve 'rapidminer.home'. Parent of jar is not a directory!");
} else {
dir = retrieveRMHomeFromLibraryDir(dir);
if (dir != null && dir.isDirectory()) {
logInfo("Gotcha! 'rapidminer.home' is: " + dir);
System.setProperty(PROPERTY_RAPIDMINER_HOME, dir.getAbsolutePath());
return true;
} else {
logSevere("Failed to retrieve 'rapidminer.home'. Parent of jar directory is not a directory!");
}
}
}
}
return false;
}
/**
* Searches for 'rapidminer.home' assuming a development environment. Start looking for
* 'rapidminer-studio' folder.
*/
private static void searchInBuildDir() {
String url = PlatformUtilities.class.getProtectionDomain().getCodeSource().getLocation().getPath();
if (url == null) {
logSevere("Failed to locate 'rapidminer.home'. Could not locate base directory of classes!");
} else {
// Use parent file, not the JAR itself
File buildDir = new File(url).getParentFile();
logInfo("Trying base directory of classes (build) '" + buildDir + "' ...");
// check if buildDir is a directory
if (buildDir != null && buildDir.isDirectory()) {
File rmHome = retrieveRMHomeFromLibraryDir(buildDir);
if (rmHome != null && rmHome.isDirectory()) {
// We have found rmHome
logInfo("Gotcha! rapidminer.home is: " + rmHome);
try {
System.setProperty(PROPERTY_RAPIDMINER_HOME, rmHome.getCanonicalPath());
} catch (IOException e) {
System.setProperty(PROPERTY_RAPIDMINER_HOME, rmHome.getAbsolutePath());
}
} else {
logSevere("Failed to locate 'rapidminer.home'! Parent dir of build directory does not exist.");
}
} else {
logSevere("Failed to locate 'rapidminer.home'! Build directory does not exists or isn't a directory.");
}
}
}
/**
*
* @param buildDir
* @return
*/
private static File retrieveRMHomeFromLibraryDir(File buildDir) {
/*
* Search for the project directory of the current build. If the search was started via
* Gradle, we are in 'target/libs/'. If it was started via an IDE we are in 'build/' or
* 'bin/'.
*/
File projectDir = null;
if (buildDir.getAbsolutePath().endsWith(TEST_ENVIRONMENT_SUFFIX)) {
// If it was started via Gradle, go two levels up so we're in the
// project's directory.
logInfo("Library located in 'target/libs/'. Assuming Gradle test task invocation.");
projectDir = buildDir.getParentFile().getParentFile();
} else {
// Otherwise assume that the search was started via IDE or from productive environment
// and use the library directory parent
projectDir = buildDir.getParentFile();
}
// Check if we are in a development or test environment (i.e. in the subproject
// 'rapidminer-studio-core' or 'rapidminer-studio-commons').
if (projectDir.getAbsolutePath().endsWith(RAPIDMINER_STUDIO_CORE)
|| projectDir.getAbsolutePath().endsWith(RAPIDMINER_STUDIO_COMMONS)) {
logInfo("Build dir located in 'rapidminer-studio-core' or 'rapidminer-studio-commons' folder. "
+ "Assuming development or testing build...");
return projectDir.getParentFile();
}
return projectDir;
}
private static void logInfo(String msg) {
LOGGER.log(Level.INFO, msg);
}
private static void logWarn(String msg) {
LOGGER.log(Level.WARNING, msg);
}
private static void logSevere(String msg) {
LOGGER.log(Level.SEVERE, msg);
}
}