/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.tuscany.runtime.standalone; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.Properties; import java.util.jar.JarFile; import org.apache.tuscany.host.runtime.TuscanyRuntime; /** * Utility class for installation directory related operations. * * @version $Revision$ $Date$ */ public final class DirectoryHelper { /** * Installation directory system property name. */ private static final String INSTALL_DIRECTORY_PROPERTY = "tuscany.installDir"; private DirectoryHelper() { } /** * Gets the installation directory based on the location of a class file. * If the system property tuscany.installDir is set then its value is used as the * location of the installation directory. Otherwise, we assume we are running from an * executable jar containing the supplied class and the installation directory is assumed to * be the parent of the directory containing that jar. * * @param clazz the class to use as a way to find the executable jar * @return directory where tuscany standalone server is installed. * @throws IllegalArgumentException if the property is set but its value is not an existing directory * @throws IllegalStateException if the location could not be determined from the location of the class file */ public static File getInstallDirectory(Class clazz) throws IllegalStateException, IllegalArgumentException { String installDirectoryPath = System.getProperty(INSTALL_DIRECTORY_PROPERTY); if (installDirectoryPath != null) { File installDirectory = new File(installDirectoryPath); if (!installDirectory.exists()) { throw new IllegalArgumentException(INSTALL_DIRECTORY_PROPERTY + " property does not refer to an existing directory: " + installDirectory); } return installDirectory; } // get the name of the Class's bytecode String name = clazz.getName(); int last = name.lastIndexOf('.'); if (last != -1) { name = name.substring(last + 1); } name = name + ".class"; // get location of the bytecode - should be a jar: URL URL url = clazz.getResource(name); if (url == null) { throw new IllegalStateException("Unable to get location of bytecode resource " + name); } String jarLocation = url.toString(); if (!jarLocation.startsWith("jar:")) { throw new IllegalStateException("Must be run from a jar: " + url); } // extract the location of thr jar from the resource URL jarLocation = jarLocation.substring(4, jarLocation.lastIndexOf("!/")); if (!jarLocation.startsWith("file:")) { throw new IllegalStateException("Must be run from a local filesystem: " + jarLocation); } File jarFile = new File(URI.create(jarLocation)); return jarFile.getParentFile().getParentFile(); } /** * Get the directory associated with a runtime profile. * If the system property tuscany.profileDir.${profileName} is set then its value * is used as the value for the profile directory. Otherwise, the directory ${installDir}/profiles/${profileName} * is used. * * @param installDir the installation directory * @param profileName tha name of the profile * @return the directory for the the specified profile * @throws FileNotFoundException if the directory does not exist */ public static File getProfileDirectory(File installDir, String profileName) throws FileNotFoundException { String propName = "tuscany.profileDir." + profileName; String profilePath = System.getProperty(propName); File profileDir; if (profilePath != null) { profileDir = new File(profilePath); } else { profileDir = new File(new File(installDir, "profiles"), profileName); } if (!profileDir.isDirectory()) { throw new FileNotFoundException("Unable to locate profile directory: " + profileDir.toString()); } return profileDir; } /** * Gets the boot directory where all the boot libraries are stored. This * is expected to be a directory named boot under the install * directory. * * @param installDirectory Tuscany install directory. * @param bootPath Boot path for the runtime. * @return Tuscany boot directory. */ public static File getBootDirectory(File installDirectory, String bootPath) { File bootDirectory = new File(installDirectory, bootPath); if (!bootDirectory.exists()) { throw new IllegalStateException("Boot directory doesn't exist: " + bootDirectory.getAbsolutePath()); } return bootDirectory; } /** * Gets the boot directory for the specified profile. * If the bootPath is not null then it is used to specify the location of the boot directory * relative to the profile directory. Otherwise, if there is a directory named "boot" relative * to the profile or install directory then it is used. * * @param installDir the installation directory * @param profileDir the profile directory * @param bootPath the path to the boot directory * @return the boot directory * @throws FileNotFoundException if the boot directory does not exist */ public static File getBootDirectory(File installDir, File profileDir, String bootPath) throws FileNotFoundException { File bootDir; if (bootPath != null) { bootDir = new File(profileDir, bootPath); } else { bootDir = new File(profileDir, "boot"); if (!bootDir.isDirectory()) { bootDir = new File(installDir, "boot"); } } if (!bootDir.isDirectory()) { throw new FileNotFoundException("Unable to locate boot directory: " + bootDir); } return bootDir; } /** * Create a classloader from all the jar files or subdirectories in a directory. * The classpath for the returned classloader will comprise all jar files and subdirectories * of the supplied directory. Hidden files and those that do not contain a valid manifest will * be silently ignored. * * @param parent the parent for the new classloader * @param directory the directory to scan * @return a classloader whose classpath includes all jar files and subdirectories of the supplied directory */ public static ClassLoader createClassLoader(ClassLoader parent, File directory) { File[] jars = directory.listFiles(new FileFilter() { public boolean accept(File file) { if (file.isHidden()) { return false; } if (file.isDirectory()) { return true; } try { JarFile jar = new JarFile(file); return jar.getManifest() != null; } catch (IOException e) { return false; } } }); URL[] urls = new URL[jars.length]; for (int i = 0; i < jars.length; i++) { try { urls[i] = jars[i].toURI().toURL(); } catch (MalformedURLException e) { // toURI should have escaped the URL throw new AssertionError(); } } return new URLClassLoader(urls, parent); } /** * Load properties from the specified file. * If the file does not exist then an empty properties object is returned. * * @param propFile the file to load from * @param defaults defaults for the properties * @return a Properties object loaded from the file * @throws IOException if there was a problem loading the properties */ public static Properties loadProperties(File propFile, Properties defaults) throws IOException { Properties props = defaults == null ? new Properties() : new Properties(defaults); FileInputStream is; try { is = new FileInputStream(propFile); } catch (FileNotFoundException e) { return props; } try { props.load(is); return props; } finally { is.close(); } } /** * Convert a File to a URL. Equivalent to file.toURI().toURL() * * @param file the file to convert * @return the URL for the File */ public static URL toURL(File file) { try { return file.toURI().toURL(); } catch (MalformedURLException e) { // toURI should have escaped this throw new AssertionError(); } } public static StandaloneRuntimeInfo createRuntimeInfo(String defaultProfile, Class markerClass) throws IOException { // get profile to use, defaulting to "launcher" String profile = System.getProperty("tuscany.profile", defaultProfile); File installDir = getInstallDirectory(markerClass); File profileDir = getProfileDirectory(installDir, profile); // load properties for this runtime File propFile = new File(profileDir, "etc/runtime.properties"); Properties props = loadProperties(propFile, System.getProperties()); // online unless the offline property is set boolean online = !Boolean.parseBoolean(props.getProperty("offline", "false")); return new StandaloneRuntimeInfoImpl(null, profile, installDir, profileDir, null, online, props); } public static TuscanyRuntime createRuntime(StandaloneRuntimeInfo runtimeInfo) throws Exception { File installDir = runtimeInfo.getInstallDirectory(); File profileDir = runtimeInfo.getProfileDirectory(); URL profileURL = toURL(profileDir); ClassLoader hostClassLoader = ClassLoader.getSystemClassLoader(); // create the classloader for booting the runtime String bootPath = runtimeInfo.getProperty("tuscany.bootDir", null); File bootDir = getBootDirectory(installDir, profileDir, bootPath); ClassLoader bootClassLoader = createClassLoader(hostClassLoader, bootDir); // locate the system SCDL URL systemSCDL = new URL(profileURL, runtimeInfo.getProperty("tuscany.systemSCDL", "system.scdl")); // locate the implementation String className = runtimeInfo.getProperty("tuscany.runtimeClass", "org.apache.tuscany.runtime.standalone.host.StandaloneRuntimeImpl"); Class implClass = Class.forName(className, true, bootClassLoader); TuscanyRuntime runtime = (TuscanyRuntime) implClass.newInstance(); runtime.setHostClassLoader(hostClassLoader); runtime.setSystemScdl(systemSCDL); runtime.setRuntimeInfo(runtimeInfo); return runtime; } }