/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2001, ThoughtWorks, Inc. * 200 E. Randolph, 25th Floor * Chicago, IL 60601 USA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * + 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. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * 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 REGENTS 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. ********************************************************************************/ /* * Copyright 2003-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. * */ package net.sourceforge.cruisecontrol.launch; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.Arrays; import net.sourceforge.cruisecontrol.launch.util.Locator; /** * Provides the means to launch CruiseControl with the appropriate classpath. * This code is based heavily on (some parts taken directly from) the Apache * Ant project. * * @author <a href="mailto:rjmpsmith@gmail.com">Robert J. Smith</a> */ public class Launcher { /** The property containing the CruiseControl home directory */ public static final String CCHOME_PROPERTY = "cc.home"; /** The directory name of the per-user CC directory */ public static final String CC_PRIVATEDIR = ".cruisecontrol"; /** The location of a per-user library directory */ public static final String CC_PRIVATELIB = "lib"; /** The location of a per-user library directory */ public static final String USER_LIBDIR = CC_PRIVATEDIR + File.separator + CC_PRIVATELIB; /** The startup class that is to be run */ public static final String MAIN_CLASS = "net.sourceforge.cruisecontrol.Main"; /** Log4j system property name. */ public static final String PROP_LOG4J_CONFIGURATION = "log4j.configuration"; private static final URL[] EMPTY_URL_ARRAY = new URL[0]; /** * Entry point for starting CruiseControl from the command line * * @param args commandline arguments */ public static void main(String[] args) { try { Launcher launcher = new Launcher(); launcher.run(args); } catch (LaunchException e) { System.err.println(e.getMessage()); } catch (Throwable t) { t.printStackTrace(); } } /** * Run the launcher * * @param args the command line arguments * * @throws LaunchException if CruiseControl home is not set or could not be located, or if other * invalid argument values are given. * @throws MalformedURLException if the URLs required for the classloader * cannot be created. */ void run(final String[] args) throws LaunchException, MalformedURLException { // First of all read the configuration final Configuration config = Configuration.getInstance(args); final File sourceJar = getClassSource(); final File distJarDir = sourceJar.getParentFile(); // // Make notice to log4j where is configuration file is final URL log4jcofig = config.getOptionUrl(Configuration.KEY_LOG4J_CONFIG); System.setProperty(PROP_LOG4J_CONFIGURATION, log4jcofig.toString()); // Process the lib dir entries found on the command line final List<URL> libPathURLs = new ArrayList<URL>(); for (final String libPath : config.getOptionStrArray(Configuration.KEY_USER_LIB_DIRS)) { addPath(libPath, true, libPathURLs); } final URL[] libJars = libPathURLs.toArray(new URL[libPathURLs.size()]); // Determine the CruiseControl directory for the distribution jars if it was provided, // Otherwise make a guess based upon the location of the launcher jar. File ccDistDir; try { ccDistDir = config.getOptionDir(Configuration.KEY_DIST_DIR); } catch (IllegalArgumentException e) { ccDistDir = distJarDir; config.getLogger().warn("Option '" + Configuration.KEY_DIST_DIR + "' not set, using " + ccDistDir.getAbsolutePath()); } File ccHome = new File(""); try { ccHome = getCCHomeDir(config, ccDistDir); } catch (LaunchException e) { ccHome = ccDistDir.getParentFile(); config.getLogger().warn("Option '" + Configuration.KEY_HOME_DIR + "' not set, using " + ccHome.getAbsolutePath()); } finally { // The property is required by other modules. It would be better to use Configuration // directly ... System.setProperty(CCHOME_PROPERTY, ccHome.getAbsolutePath()); } // Determine CruiseControl library directory for third party jars, if it was provided. // Otherwise make a guess based upon the CruiseControl home dir we found earlier. File ccLibDir; try { ccLibDir = config.getOptionDir(Configuration.KEY_LIBRARY_DIR); } catch (IllegalArgumentException e) { ccLibDir = new File(ccHome, "lib"); } final URL[] distJars = Locator.getLocationURLs(ccDistDir); final URL[] supportJars = Locator.getLocationURLs(ccLibDir); final URL[] antJars = Locator.getLocationURLs(new File(ccLibDir, "ant")); // Locate any jars in the per-user lib directory final File userLibDir = new File(ccHome, USER_LIBDIR); final boolean noUserLib = config.getOptionBool(Configuration.KEY_NO_USER_LIB); final URL[] userJars = noUserLib ? EMPTY_URL_ARRAY : Locator.getLocationURLs(userLibDir); // Locate the Java tools jar final File toolsJar = Locator.getToolsJar(); // Concatenate our jar lists - order of precedence will be those jars // specified on the command line followed by jars in the per-user // lib directory and finally those jars found in the dist and lib // folders of CruiseControl home. int numJars = libJars.length + userJars.length + distJars.length + supportJars.length + antJars.length; if (toolsJar != null) { numJars++; } final URL[] jars = new URL[numJars]; copyJarUrls(libJars, jars, 0); copyJarUrls(userJars, jars, libJars.length); copyJarUrls(distJars, jars, userJars.length + libJars.length); copyJarUrls(supportJars, jars, userJars.length + libJars.length + distJars.length); copyJarUrls(antJars, jars, userJars.length + libJars.length + distJars.length + supportJars.length); if (toolsJar != null) { jars[jars.length - 1] = toolsJar.toURI().toURL(); } // Update the JVM java.class.path property final StringBuffer baseClassPath = new StringBuffer(System.getProperty("java.class.path")); if (baseClassPath.charAt(baseClassPath.length() - 1) == File.pathSeparatorChar) { baseClassPath.setLength(baseClassPath.length() - 1); } for (final URL jar : jars) { baseClassPath.append(File.pathSeparatorChar); baseClassPath.append(Locator.fromURI(jar.toString())); } baseClassPath.append(File.pathSeparatorChar); baseClassPath.append("."); // adding the homedirectory to the classpath baseClassPath.append(File.pathSeparatorChar); baseClassPath.append(ccHome.getAbsolutePath()).append(File.separatorChar); System.setProperty("java.class.path", baseClassPath.toString()); config.getLogger().info("Classpath: " + baseClassPath.toString()); // Create a new class loader which has access to our jars final URLClassLoader loader = new URLClassLoader(jars); Thread.currentThread().setContextClassLoader(loader); // Launch CruiseControl! try { final Class mainClass = loader.loadClass(MAIN_CLASS); final CruiseControlMain main = (CruiseControlMain) mainClass.newInstance(); final boolean normalExit = main.start(config); if (!normalExit) { exitWithErrorCode(); } } catch (Throwable t) { t.printStackTrace(); } } /** @return the path to Jar (or directory) where Launcher.class file is located */ File getClassSource() { return Locator.getClassSource(Launcher.class); } /** * When called, invokes System.exit(1). The method is protected to be overridden in tests. */ protected void exitWithErrorCode() { System.exit(1); } /** Exception message if CC Home directory couldn't be determined. */ static final String MSG_BAD_CCHOME = "CruiseControl home is not set or could not be located."; /** * Determine and return the CC Home directory, and reset the * {@link #CCHOME_PROPERTY} to match if needed. * @param distJarDir the main CC dist directory containing * cruisecontrol.jar and cruisecontrol-launcher.jar, used to guess default home dir. * @return CruiseControl home directory * @throws LaunchException if CruiseControl home is not set or could not be located. */ File getCCHomeDir(Configuration conf, File distJarDir) throws LaunchException { // Check, if the directory was defined in a configuration try { return conf.getOptionDir(Configuration.KEY_HOME_DIR); } catch (IllegalArgumentException e) { // Was not defined correctly or not found ... } // If the location was not specified, or it does not exist, try to guess // the location based upon the location of the launcher Jar. conf.getLogger().warn("Trying to guess '" + Configuration.KEY_HOME_DIR + "' from '" + distJarDir.getAbsolutePath()); if (distJarDir.getParentFile() != null) { return distJarDir.getParentFile(); } // If none of the above worked, give up now. throw new LaunchException(MSG_BAD_CCHOME); } private void copyJarUrls(URL[] sourceArray, URL[] destinationArray, int destinationStartIndex) { System.arraycopy(sourceArray, 0, destinationArray, destinationStartIndex, sourceArray.length); } /** * Add a CLASSPATH or -lib to lib path urls. * @param path the classpath or lib path to add to the libPathULRLs * @param getJars if true and a path is a directory, add the jars in * the directory to the path urls * @param libPathURLs the list of paths to add to * @throws MalformedURLException if the URLs required for the classloader * cannot be created. */ private void addPath(final String path, final boolean getJars, final List<URL> libPathURLs) throws MalformedURLException { final StringTokenizer myTokenizer = new StringTokenizer(path, System.getProperty("path.separator")); while (myTokenizer.hasMoreElements()) { final String elementName = myTokenizer.nextToken(); final File element = new File(elementName); if (elementName.indexOf("%") != -1 && !element.exists()) { continue; } if (getJars && element.isDirectory()) { // add any jars in the directory final URL[] dirURLs = Locator.getLocationURLs(element); libPathURLs.addAll(Arrays.asList(dirURLs)); } libPathURLs.add(element.toURI().toURL()); } } }