From: <to...@us...> - 2007-01-30 00:03:46
|
Revision: 12 http://techne-dev.svn.sourceforge.net/techne-dev/?rev=12&view=rev Author: tonit Date: 2007-01-29 16:03:45 -0800 (Mon, 29 Jan 2007) Log Message: ----------- fi Added Paths: ----------- sandbox/tonit/techne.launch/src/techne/launch/main/Main.java Added: sandbox/tonit/techne.launch/src/techne/launch/main/Main.java =================================================================== --- sandbox/tonit/techne.launch/src/techne/launch/main/Main.java (rev 0) +++ sandbox/tonit/techne.launch/src/techne/launch/main/Main.java 2007-01-30 00:03:45 UTC (rev 12) @@ -0,0 +1,684 @@ +/* + * 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 techne.launch.main; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.*; +import java.util.*; + +import org.apache.felix.framework.Felix; +import org.apache.felix.framework.cache.BundleCache; +import org.apache.felix.framework.util.MutablePropertyResolverImpl; +import org.apache.felix.framework.util.StringMap; + +/** + * <p> + * This class is the default way to instantiate and execute the framework. It is not + * intended to be the only way to instantiate and execute the framework; rather, it is + * one example of how to do so. When embedding the framework in a host application, + * this class can serve as a simple guide of how to do so. It may even be + * worthwhile to reuse some of its property handling capabilities. This class + * is completely static and is only intended to start a single instance of + * the framework. + * </p> +**/ +public class Main +{ + /** + * The system property name used to specify an URL to the system + * property file. + **/ + public static final String SYSTEM_PROPERTIES_PROP = "felix.system.properties"; + /** + * The default name used for the system properties file. + **/ + public static final String SYSTEM_PROPERTIES_FILE_VALUE = "system.properties"; + /** + * The system property name used to specify an URL to the configuration + * property file to be used for the created the framework instance. + **/ + public static final String CONFIG_PROPERTIES_PROP = "felix.config.properties"; + /** + * The default name used for the configuration properties file. + **/ + public static final String CONFIG_PROPERTIES_FILE_VALUE = "config.properties"; + + public static final String KEYSTORE_FILE_PROP = "felix.keystore"; + + public static final String KEYSTORE_FILE_VALUE = System.getProperty("java.home") + + File.separatorChar + "lib" + File.separatorChar + "security" + + File.separatorChar + "cacerts" + File.pathSeparatorChar + System.getProperty("user.home") + + File.separatorChar + ".keystore"; + + public static final String KEYSTORE_TYPE_PROP = "felix.keystore.type"; + + public static final String KEYSTORE_TYPE_VALUE = "JKS" + File.pathSeparatorChar + "JKS"; + + public static final String KEYSTORE_PASS_PROP = "felix.keystore.pass"; + + public static final String KEYSTORE_PASS_VALUE = "changeit" + File.pathSeparatorChar + "changeit"; + + private static Felix m_felix = null; + + /** + * <p> + * This method performs the main task of constructing an framework instance + * and starting its execution. The following functions are performed + * when invoked: + * </p> + * <ol> + * <li><i><b>Read the system properties file.<b></i> This is a file + * containing properties to be pushed into <tt>System.setProperty()</tt> + * before starting the framework. This mechanism is mainly shorthand + * for people starting the framework from the command line to avoid having + * to specify a bunch of <tt>-D</tt> system property definitions. + * The only properties defined in this file that will impact the framework's + * behavior are the those concerning setting HTTP proxies, such as + * <tt>http.proxyHost</tt>, <tt>http.proxyPort</tt>, and + * <tt>http.proxyAuth</tt>. + * </li> + * <li><i><b>Perform system property variable substitution on system + * properties.</b></i> Any system properties in the system property + * file whose value adheres to <tt>${<system-prop-name>}</tt> + * syntax will have their value substituted with the appropriate + * system property value. + * </li> + * <li><i><b>Read the framework's configuration property file.</b></i> This is + * a file containing properties used to configure the framework + * instance and to pass configuration information into + * bundles installed into the framework instance. The configuration + * property file is called <tt>config.properties</tt> by default + * and is located in the same directory as the <tt>felix.jar</tt> + * file, which is typically in the <tt>lib/</tt> directory of the + * Felix installation directory. It is possible to use a different + * location for the property file by specifying the desired URL + * using the <tt>felix.config.properties</tt> system property; + * this should be set using the <tt>-D</tt> syntax when executing + * the JVM. Refer to the + * <a href="Felix.html#start(org.apache.felix.framework.util.MutablePropertyResolver, org.apache.felix.framework.util.MutablePropertyResolver, java.util.List)"> + * <tt>Felix.start()</tt></a> method documentation for more + * information on the framework configuration options. + * </li> + * <li><i><b>Perform system property variable substitution on configuration + * properties.</b></i> Any configuration properties whose value adheres to + * <tt>${<system-prop-name>}</tt> syntax will have their value + * substituted with the appropriate system property value. + * </li> + * <li><i><b>Ensure the default bundle cache has sufficient information to + * initialize.</b></i> The default implementation of the bundle cache + * requires either a profile name or a profile directory in order to + * start. The configuration properties are checked for at least one + * of the <tt>felix.cache.profile</tt> or <tt>felix.cache.profiledir</tt> + * properties. If neither is found, the user is asked to supply a profile + * name that is added to the configuration property set. See the + * <a href="cache/DefaultBundleCache.html"><tt>DefaultBundleCache</tt></a> + * documentation for more details its configuration options. + * </li> + * <li><i><b>Creates and starts a framework instance.</b></i> A simple + * <a href="util/MutablePropertyResolver.html"><tt>MutablePropertyResolver</tt></a> + * is created for the configuration property file and is passed + * into the framework when it is started. + * </li> + * </ol> + * <p> + * It should be noted that simply starting an instance of the framework is not enough + * to create an interactive session with it. It is necessary to install + * and start bundles that provide an interactive impl; this is generally + * done by specifying an "auto-start" property in the framework configuration + * property file. If no interactive impl bundles are installed or if + * the configuration property file cannot be found, the framework will appear to + * be hung or deadlocked. This is not the case, it is executing correctly, + * there is just no way to interact with it. Refer to the + * <a href="Felix.html#start(org.apache.felix.framework.util.MutablePropertyResolver, org.apache.felix.framework.util.MutablePropertyResolver, java.util.List)"> + * <tt>Felix.start()</tt></a> method documentation for more information on + * framework configuration options. + * </p> + * @param argv An array of arguments, all of which are ignored. + * @throws Exception If an error occurs. + **/ + public static void main(String[] argv) throws Exception + { + System.out.println("Base dir: " + new File(".").getAbsolutePath()); + // Load system properties. + Main.loadSystemProperties(); + + // Read configuration properties. + Properties configProps = Main.loadConfigProperties(); + + // See if the profile name property was specified. + String profileName = configProps.getProperty(BundleCache.CACHE_PROFILE_PROP); + + // See if the profile directory property was specified. + String profileDirName = configProps.getProperty(BundleCache.CACHE_PROFILE_DIR_PROP); + + // Print welcome banner. + System.out.println("\nWelcome to Techne Runtime"); + System.out.println("============================\n"); + + // If no profile or profile directory is specified in the + // properties, then ask for a profile name. + if ((profileName == null) && (profileDirName == null)) + { + System.out.print("Enter profile name: "); + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + try + { + profileName = in.readLine(); + } + catch (IOException ex) + { + System.err.println("Could not read input."); + System.exit(-1); + } + /** + * @author tmenzel: we may got null from in.readline() + */ + if (profileName == null) profileName = ""; + + System.out.println(""); + if ( profileName.length() != 0) + { + configProps.setProperty(BundleCache.CACHE_PROFILE_PROP, profileName); + } + } + + // A profile directory or name must be specified. + if ((profileDirName == null) && (profileName.length() == 0)) + { + System.err.println("You must specify a profile name or directory."); + System.exit(-1); + } + + try + { + // Now create an instance of the framework. + System.out.println("Thank you, now creating felix framework instance.."); + m_felix = new Felix(); + m_felix.start( + new MutablePropertyResolverImpl(new StringMap(configProps, false)), + null, (System.getSecurityManager() == null) ? null : new TrustManager(configProps)); + System.out.println("felix done"); + } + catch (Exception ex) + { + System.err.println("Could not create framework: " + ex); + ex.printStackTrace(); + System.exit(-1); + } + } + + /** + * <p> + * Loads the properties in the system property file associated with the + * framework installation into <tt>System.setProperty()</tt>. These properties + * are not directly used by the framework in anyway. By default, the system + * property file is located in the <tt>conf/</tt> directory of the Felix + * installation directory and is called "<tt>system.properties</tt>". The + * installation directory of Felix is assumed to be the parent directory of + * the <tt>felix.jar</tt> file as found on the system class path property. + * The precise file from which to load system properties can be set by + * initializing the "<tt>felix.system.properties</tt>" system property to an + * arbitrary URL. + * </p> + **/ + public static void loadSystemProperties() + { + // The system properties file is either specified by a system + // property or it is in the same directory as the Felix JAR file. + // Try to load it from one of these places. + + // See if the property URL was specified as a property. + URL propURL = null; + String custom = System.getProperty(SYSTEM_PROPERTIES_PROP); + if (custom != null) + { + try + { + propURL = new URL(custom); + } + catch (MalformedURLException ex) + { + System.err.print("Main: " + ex); + return; + } + } + else + { + // Determine where the configuration directory is by figuring + // out where felix.jar is located on the system class path. + File confDir = null; + String classpath = System.getProperty("java.class.path"); + int index = classpath.toLowerCase().indexOf("techne.jar"); + int start = classpath.lastIndexOf(File.pathSeparator, index) + 1; + if (index > start) + { + String jarLocation = classpath.substring(start, index); + if (jarLocation.length() == 0) + { + jarLocation = "."; + } + confDir = new File(new File(jarLocation).getParent(), "conf"); + } + else + { + // Can't figure it out so use the current directory as default. + confDir = new File(System.getProperty("user.dir")); + } + + try + { + propURL = new File(confDir, SYSTEM_PROPERTIES_FILE_VALUE).toURL(); + } + catch (MalformedURLException ex) + { + System.err.print("Main: " + ex); + return; + } + } + + // Read the properties file. + Properties props = new Properties(); + InputStream is = null; + try + { + is = propURL.openConnection().getInputStream(); + props.load(is); + is.close(); + } + catch (FileNotFoundException ex) + { + // Ignore file not found. + } + catch (Exception ex) + { + System.err.println( + "Main: Error loading system properties from " + propURL); + System.err.println("Main: " + ex); + try + { + if (is != null) is.close(); + } + catch (IOException ex2) + { + // Nothing we can do. + } + return; + } + + // Perform variable substitution on specified properties. + for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) + { + String name = (String) e.nextElement(); + System.setProperty(name, + substVars(props.getProperty(name), name, null, null)); + } + } + + /** + * <p> + * Loads the configuration properties in the configuration property file + * associated with the framework installation; these properties + * are accessible to the framework and to bundles and are intended + * for configuration purposes. By default, the configuration property + * file is located in the <tt>conf/</tt> directory of the Felix + * installation directory and is called "<tt>config.properties</tt>". + * The installation directory of Felix is assumed to be the parent + * directory of the <tt>felix.jar</tt> file as found on the system class + * path property. The precise file from which to load configuration + * properties can be set by initializing the "<tt>felix.config.properties</tt>" + * system property to an arbitrary URL. + * </p> + * @return A <tt>Properties</tt> instance or <tt>null</tt> if there was an error. + **/ + public static Properties loadConfigProperties() + { + // The config properties file is either specified by a system + // property or it is in the same directory as the Felix JAR file. + // Try to load it from one of these places. + + // See if the property URL was specified as a property. + URL propURL = null; + String custom = System.getProperty(CONFIG_PROPERTIES_PROP); + if (custom != null) + { + try + { + propURL = new URL(custom); + } + catch (MalformedURLException ex) + { + System.err.print("Main: " + ex); + return null; + } + } + else + { + // Determine where the configuration directory is by figuring + // out where felix.jar is located on the system class path. + File confDir = null; + String classpath = System.getProperty("java.class.path"); + int index = classpath.toLowerCase().indexOf("techne.jar"); + int start = classpath.lastIndexOf(File.pathSeparator, index) + 1; + if (index > start) + { + String jarLocation = classpath.substring(start, index); + if (jarLocation.length() == 0) + { + jarLocation = "."; + } + confDir = new File(new File(jarLocation).getParent(), "conf"); + } + else + { + // Can't figure it out so use the current directory as default. + confDir = new File(System.getProperty("user.dir")); + } + + try + { + propURL = new File(confDir, CONFIG_PROPERTIES_FILE_VALUE).toURL(); + } + catch (MalformedURLException ex) + { + System.err.print("Main: " + ex); + return null; + } + } + + // Read the properties file. + Properties props = new Properties(); + InputStream is = null; + try + { + is = propURL.openConnection().getInputStream(); + props.load(is); + is.close(); + } + catch (FileNotFoundException ex) + { + // Ignore file not found. + } + catch (Exception ex) + { + System.err.println( + "Error loading config properties from " + propURL); + System.err.println("Main: " + ex); + try + { + if (is != null) is.close(); + } + catch (IOException ex2) + { + // Nothing we can do. + } + return null; + } + + // Perform variable substitution for system properties. + for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) + { + String name = (String) e.nextElement(); + props.setProperty(name, + substVars(props.getProperty(name), name, null, props)); + } + + return props; + } + + private static final String DELIM_START = "${"; + private static final String DELIM_STOP = "}"; + + /** + * <p> + * This method performs property variable substitution on the + * specified value. If the specified value contains the syntax + * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt> + * refers to either a configuration property or a system property, + * then the corresponding property value is substituted for the variable + * placeholder. Multiple variable placeholders may exist in the + * specified value as well as nested variable placeholders, which + * are substituted from inner most to outer most. Configuration + * properties override system properties. + * </p> + * @param val The string on which to perform property substitution. + * @param currentKey The key of the property being evaluated used to + * detect cycles. + * @param cycleMap Map of variable references used to detect nested cycles. + * @param configProps Set of configuration properties. + * @return The value of the specified string after system property substitution. + * @throws IllegalArgumentException If there was a syntax error in the + * property placeholder syntax or a recursive variable reference. + **/ + public static String substVars(String val, String currentKey, + Map cycleMap, Properties configProps) + throws IllegalArgumentException + { + // If there is currently no cycle map, then create + // one for detecting cycles for this invocation. + if (cycleMap == null) + { + cycleMap = new HashMap(); + } + + // Put the current key in the cycle map. + cycleMap.put(currentKey, currentKey); + + // Assume we have a value that is something like: + // "leading ${foo.${bar}} middle ${baz} trailing" + + // Find the first ending '}' variable delimiter, which + // will correspond to the first deepest nested variable + // placeholder. + int stopDelim = val.indexOf(DELIM_STOP); + + // Find the matching starting "${" variable delimiter + // by looping until we find a start delimiter that is + // greater than the stop delimiter we have found. + int startDelim = val.indexOf(DELIM_START); + while (stopDelim >= 0) + { + int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length()); + if ((idx < 0) || (idx > stopDelim)) + { + break; + } + else if (idx < stopDelim) + { + startDelim = idx; + } + } + + // If we do not have a start or stop delimiter, then just + // return the existing value. + if ((startDelim < 0) && (stopDelim < 0)) + { + return val; + } + // At this point, we found a stop delimiter without a start, + // so throw an exception. + else if (((startDelim < 0) || (startDelim > stopDelim)) + && (stopDelim >= 0)) + { + throw new IllegalArgumentException( + "stop delimiter with no start delimiter: " + + val); + } + + // At this point, we have found a variable placeholder so + // we must perform a variable substitution on it. + // Using the start and stop delimiter indices, extract + // the first, deepest nested variable placeholder. + String variable = + val.substring(startDelim + DELIM_START.length(), stopDelim); + + // Verify that this is not a recursive variable reference. + if (cycleMap.get(variable) != null) + { + throw new IllegalArgumentException( + "recursive variable reference: " + variable); + } + + // Get the value of the deepest nested variable placeholder. + // Try to configuration properties first. + String substValue = (configProps != null) + ? configProps.getProperty(variable, null) + : null; + if (substValue == null) + { + // Ignore unknown property values. + substValue = System.getProperty(variable, ""); + } + + // Remove the found variable from the cycle map, since + // it may appear more than once in the value and we don't + // want such situations to appear as a recursive reference. + cycleMap.remove(variable); + + // Append the leading characters, the substituted value of + // the variable, and the trailing characters to get the new + // value. + val = val.substring(0, startDelim) + + substValue + + val.substring(stopDelim + DELIM_STOP.length(), val.length()); + + // Now perform substitution again, since there could still + // be substitutions to make. + val = substVars(val, currentKey, cycleMap, configProps); + + // Return the value. + return val; + } + + private static class TrustManager extends AbstractCollection + { + private String[] m_keystores = null; + private String[] m_passwds = null; + private String[] m_types = null; + private ArrayList m_stores = null; + + TrustManager(Properties config) + { + StringTokenizer tok = new StringTokenizer(System.getProperty(KEYSTORE_FILE_PROP, + config.getProperty(KEYSTORE_FILE_PROP, KEYSTORE_FILE_VALUE)), File.pathSeparator); + + m_keystores = new String[tok.countTokens()]; + + for (int i = 0;tok.hasMoreTokens();i++) + { + m_keystores[i] = tok.nextToken(); + } + + tok = new StringTokenizer(System.getProperty(KEYSTORE_PASS_PROP, + config.getProperty(KEYSTORE_PASS_PROP, KEYSTORE_PASS_VALUE)), File.pathSeparator); + + m_passwds = new String[tok.countTokens()]; + + for (int i = 0;tok.hasMoreTokens();i++) + { + m_passwds[i] = tok.nextToken(); + } + + tok = new StringTokenizer(System.getProperty(KEYSTORE_TYPE_PROP, + config.getProperty(KEYSTORE_TYPE_PROP, KEYSTORE_TYPE_VALUE)), File.pathSeparator); + + m_types = new String[tok.countTokens()]; + + for (int i = 0;tok.hasMoreTokens();i++) + { + m_types[i] = tok.nextToken(); + } + } + + public synchronized Iterator iterator() + { + if (m_stores == null) + { + loadStores(); + } + + return m_stores.iterator(); + } + + public synchronized int size() + { + if (m_stores == null) + { + loadStores(); + } + + return m_stores.size(); + } + + private void loadStores() + { + m_stores = new ArrayList(); + + if ((m_keystores.length == m_passwds.length) && (m_passwds.length == m_types.length) + && (System.getSecurityManager() != null)) + { + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + List certs = new ArrayList(); + + for (int i = 0;i < m_keystores.length;i++) + { + + try + { + KeyStore ks = KeyStore.getInstance(m_types[i]); + ks.load(new FileInputStream(m_keystores[i]), m_passwds[i].toCharArray()); + for (Enumeration e = ks.aliases(); e.hasMoreElements();) + { + String alias = (String) e.nextElement(); + if (ks.isCertificateEntry(alias)) + { + certs.add(ks.getCertificate(alias)); + } + } + } + catch (Exception ex) + { + certs.clear(); + ex.printStackTrace(System.err); + + System.err.println("WARNING: Error accessing keystore: " + m_keystores[i]); + } + + if (!certs.isEmpty()) + { + m_stores.addAll(certs); + certs.clear(); + } + } + + return null; + } + }); + } + if (m_stores.isEmpty()) + { + System.err.println("WARNING: No trusted CA certificates!"); + } + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |