001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.felix.main;
020
021 import java.io.*;
022 import java.net.MalformedURLException;
023 import java.net.URL;
024 import java.util.*;
025 import org.apache.felix.framework.util.Util;
026 import org.osgi.framework.Constants;
027 import org.osgi.framework.launch.Framework;
028 import org.osgi.framework.launch.FrameworkFactory;
029
030 /**
031 * <p>
032 * This class is the default way to instantiate and execute the framework. It is not
033 * intended to be the only way to instantiate and execute the framework; rather, it is
034 * one example of how to do so. When embedding the framework in a host application,
035 * this class can serve as a simple guide of how to do so. It may even be
036 * worthwhile to reuse some of its property handling capabilities.
037 * </p>
038 **/
039 public class Main
040 {
041 /**
042 * Switch for specifying bundle directory.
043 **/
044 public static final String BUNDLE_DIR_SWITCH = "-b";
045
046 /**
047 * The property name used to specify whether the launcher should
048 * install a shutdown hook.
049 **/
050 public static final String SHUTDOWN_HOOK_PROP = "felix.shutdown.hook";
051 /**
052 * The property name used to specify an URL to the system
053 * property file.
054 **/
055 public static final String SYSTEM_PROPERTIES_PROP = "felix.system.properties";
056 /**
057 * The default name used for the system properties file.
058 **/
059 public static final String SYSTEM_PROPERTIES_FILE_VALUE = "system.properties";
060 /**
061 * The property name used to specify an URL to the configuration
062 * property file to be used for the created the framework instance.
063 **/
064 public static final String CONFIG_PROPERTIES_PROP = "felix.config.properties";
065 /**
066 * The default name used for the configuration properties file.
067 **/
068 public static final String CONFIG_PROPERTIES_FILE_VALUE = "config.properties";
069 /**
070 * Name of the configuration directory.
071 */
072 public static final String CONFIG_DIRECTORY = "conf";
073
074 private static Framework m_fwk = null;
075
076 /**
077 * <p>
078 * This method performs the main task of constructing an framework instance
079 * and starting its execution. The following functions are performed
080 * when invoked:
081 * </p>
082 * <ol>
083 * <li><i><b>Examine and verify command-line arguments.</b></i> The launcher
084 * accepts a "<tt>-b</tt>" command line switch to set the bundle auto-deploy
085 * directory and a single argument to set the bundle cache directory.
086 * </li>
087 * <li><i><b>Read the system properties file.</b></i> This is a file
088 * containing properties to be pushed into <tt>System.setProperty()</tt>
089 * before starting the framework. This mechanism is mainly shorthand
090 * for people starting the framework from the command line to avoid having
091 * to specify a bunch of <tt>-D</tt> system property definitions.
092 * The only properties defined in this file that will impact the framework's
093 * behavior are the those concerning setting HTTP proxies, such as
094 * <tt>http.proxyHost</tt>, <tt>http.proxyPort</tt>, and
095 * <tt>http.proxyAuth</tt>. Generally speaking, the framework does
096 * not use system properties at all.
097 * </li>
098 * <li><i><b>Read the framework's configuration property file.</b></i> This is
099 * a file containing properties used to configure the framework
100 * instance and to pass configuration information into
101 * bundles installed into the framework instance. The configuration
102 * property file is called <tt>config.properties</tt> by default
103 * and is located in the <tt>conf/</tt> directory of the Felix
104 * installation directory, which is the parent directory of the
105 * directory containing the <tt>felix.jar</tt> file. It is possible
106 * to use a different location for the property file by specifying
107 * the desired URL using the <tt>felix.config.properties</tt>
108 * system property; this should be set using the <tt>-D</tt> syntax
109 * when executing the JVM. If the <tt>config.properties</tt> file
110 * cannot be found, then default values are used for all configuration
111 * properties. Refer to the
112 * <a href="Felix.html#Felix(java.util.Map)"><tt>Felix</tt></a>
113 * constructor documentation for more information on framework
114 * configuration properties.
115 * </li>
116 * <li><i><b>Copy configuration properties specified as system properties
117 * into the set of configuration properties.</b></i> Even though the
118 * Felix framework does not consult system properties for configuration
119 * information, sometimes it is convenient to specify them on the command
120 * line when launching Felix. To make this possible, the Felix launcher
121 * copies any configuration properties specified as system properties
122 * into the set of configuration properties passed into Felix.
123 * </li>
124 * <li><i><b>Add shutdown hook.</b></i> To make sure the framework shutdowns
125 * cleanly, the launcher installs a shutdown hook; this can be disabled
126 * with the <tt>felix.shutdown.hook</tt> configuration property.
127 * </li>
128 * <li><i><b>Create and initialize a framework instance.</b></i> The OSGi standard
129 * <tt>FrameworkFactory</tt> is retrieved from <tt>META-INF/services</tt>
130 * and used to create a framework instance with the configuration properties.
131 * </li>
132 * <li><i><b>Auto-deploy bundles.</b></i> All bundles in the auto-deploy
133 * directory are deployed into the framework instance.
134 * </li>
135 * <li><i><b>Start the framework.</b></i> The framework is started and
136 * the launcher thread waits for the framework to shutdown.
137 * </li>
138 * </ol>
139 * <p>
140 * It should be noted that simply starting an instance of the framework is not
141 * enough to create an interactive session with it. It is necessary to install
142 * and start bundles that provide a some means to interact with the framework;
143 * this is generally done by bundles in the auto-deploy directory or specifying
144 * an "auto-start" property in the configuration property file. If no bundles
145 * providing a means to interact with the framework are installed or if the
146 * configuration property file cannot be found, the framework will appear to
147 * be hung or deadlocked. This is not the case, it is executing correctly,
148 * there is just no way to interact with it.
149 * </p>
150 * <p>
151 * The launcher provides two ways to deploy bundles into a framework at
152 * startup, which have associated configuration properties:
153 * </p>
154 * <ul>
155 * <li>Bundle auto-deploy - Automatically deploys all bundles from a
156 * specified directory, controlled by the following configuration
157 * properties:
158 * <ul>
159 * <li><tt>felix.auto.deploy.dir</tt> - Specifies the auto-deploy directory
160 * from which bundles are automatically deploy at framework startup.
161 * The default is the <tt>bundle/</tt> directory of the current directory.
162 * </li>
163 * <li><tt>felix.auto.deploy.action</tt> - Specifies the auto-deploy actions
164 * to be found on bundle JAR files found in the auto-deploy directory.
165 * The possible actions are <tt>install</tt>, <tt>update</tt>,
166 * <tt>start</tt>, and <tt>uninstall</tt>. If no actions are specified,
167 * then the auto-deploy directory is not processed. There is no default
168 * value for this property.
169 * </li>
170 * </ul>
171 * </li>
172 * <li>Bundle auto-properties - Configuration properties which specify URLs
173 * to bundles to install/start:
174 * <ul>
175 * <li><tt>felix.auto.install.N</tt> - Space-delimited list of bundle
176 * URLs to automatically install when the framework is started,
177 * where <tt>N</tt> is the start level into which the bundle will be
178 * installed (e.g., felix.auto.install.2).
179 * </li>
180 * <li><tt>felix.auto.start.N</tt> - Space-delimited list of bundle URLs
181 * to automatically install and start when the framework is started,
182 * where <tt>N</tt> is the start level into which the bundle will be
183 * installed (e.g., felix.auto.start.2).
184 * </li>
185 * </ul>
186 * </li>
187 * </ul>
188 * <p>
189 * These properties should be specified in the <tt>config.properties</tt>
190 * so that they can be processed by the launcher during the framework
191 * startup process.
192 * </p>
193 * @param args Accepts arguments to set the auto-deploy directory and/or
194 * the bundle cache directory.
195 * @throws Exception If an error occurs.
196 **/
197 public static void main(String[] args) throws Exception
198 {
199 // Look for bundle directory and/or cache directory.
200 // We support at most one argument, which is the bundle
201 // cache directory.
202 String bundleDir = null;
203 String cacheDir = null;
204 boolean expectBundleDir = false;
205 for (int i = 0; i < args.length; i++)
206 {
207 if (args[i].equals(BUNDLE_DIR_SWITCH))
208 {
209 expectBundleDir = true;
210 }
211 else if (expectBundleDir)
212 {
213 bundleDir = args[i];
214 expectBundleDir = false;
215 }
216 else
217 {
218 cacheDir = args[i];
219 }
220 }
221
222 if ((args.length > 3) || (expectBundleDir && bundleDir == null))
223 {
224 System.out.println("Usage: [-b <bundle-deploy-dir>] [<bundle-cache-dir>]");
225 System.exit(0);
226 }
227
228 // Load system properties.
229 Main.loadSystemProperties();
230
231 // Read configuration properties.
232 Properties configProps = Main.loadConfigProperties();
233 // If no configuration properties were found, then create
234 // an empty properties object.
235 if (configProps == null)
236 {
237 System.err.println("No " + CONFIG_PROPERTIES_FILE_VALUE + " found.");
238 configProps = new Properties();
239 }
240
241 // Copy framework properties from the system properties.
242 Main.copySystemProperties(configProps);
243
244 // If there is a passed in bundle auto-deploy directory, then
245 // that overwrites anything in the config file.
246 if (bundleDir != null)
247 {
248 configProps.setProperty(AutoProcessor.AUTO_DEPLOY_DIR_PROPERY, bundleDir);
249 }
250
251 // If there is a passed in bundle cache directory, then
252 // that overwrites anything in the config file.
253 if (cacheDir != null)
254 {
255 configProps.setProperty(Constants.FRAMEWORK_STORAGE, cacheDir);
256 }
257
258 // If enabled, register a shutdown hook to make sure the framework is
259 // cleanly shutdown when the VM exits.
260 String enableHook = configProps.getProperty(SHUTDOWN_HOOK_PROP);
261 if ((enableHook == null) || !enableHook.equalsIgnoreCase("false"))
262 {
263 Runtime.getRuntime().addShutdownHook(new Thread("Felix Shutdown Hook") {
264 public void run()
265 {
266 try
267 {
268 if (m_fwk != null)
269 {
270 m_fwk.stop();
271 m_fwk.waitForStop(0);
272 }
273 }
274 catch (Exception ex)
275 {
276 System.err.println("Error stopping framework: " + ex);
277 }
278 }
279 });
280 }
281
282 // Print welcome banner.
283 System.out.println("\nWelcome to Felix");
284 System.out.println("================\n");
285
286 try
287 {
288 // Create an instance of the framework.
289 FrameworkFactory factory = getFrameworkFactory();
290 m_fwk = factory.newFramework(configProps);
291 // Initialize the framework, but don't start it yet.
292 m_fwk.init();
293 // Use the system bundle context to process the auto-deploy
294 // and auto-install/auto-start properties.
295 AutoProcessor.process(configProps, m_fwk.getBundleContext());
296 // Start the framework.
297 m_fwk.start();
298 // Wait for framework to stop to exit the VM.
299 m_fwk.waitForStop(0);
300 System.exit(0);
301 }
302 catch (Exception ex)
303 {
304 System.err.println("Could not create framework: " + ex);
305 ex.printStackTrace();
306 System.exit(-1);
307 }
308 }
309
310 /**
311 * Simple method to parse META-INF/services file for framework factory.
312 * Currently, it assumes the first non-commented line is the class name
313 * of the framework factory implementation.
314 * @return The created <tt>FrameworkFactory</tt> instance.
315 * @throws Exception if any errors occur.
316 **/
317 private static FrameworkFactory getFrameworkFactory() throws Exception
318 {
319 URL url = Main.class.getClassLoader().getResource(
320 "META-INF/services/org.osgi.framework.launch.FrameworkFactory");
321 if (url != null)
322 {
323 BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
324 try
325 {
326 for (String s = br.readLine(); s != null; s = br.readLine())
327 {
328 s = s.trim();
329 // Try to load first non-empty, non-commented line.
330 if ((s.length() > 0) && (s.charAt(0) != '#'))
331 {
332 return (FrameworkFactory) Class.forName(s).newInstance();
333 }
334 }
335 }
336 finally
337 {
338 if (br != null) br.close();
339 }
340 }
341
342 throw new Exception("Could not find framework factory.");
343 }
344
345 /**
346 * <p>
347 * Loads the properties in the system property file associated with the
348 * framework installation into <tt>System.setProperty()</tt>. These properties
349 * are not directly used by the framework in anyway. By default, the system
350 * property file is located in the <tt>conf/</tt> directory of the Felix
351 * installation directory and is called "<tt>system.properties</tt>". The
352 * installation directory of Felix is assumed to be the parent directory of
353 * the <tt>felix.jar</tt> file as found on the system class path property.
354 * The precise file from which to load system properties can be set by
355 * initializing the "<tt>felix.system.properties</tt>" system property to an
356 * arbitrary URL.
357 * </p>
358 **/
359 public static void loadSystemProperties()
360 {
361 // The system properties file is either specified by a system
362 // property or it is in the same directory as the Felix JAR file.
363 // Try to load it from one of these places.
364
365 // See if the property URL was specified as a property.
366 URL propURL = null;
367 String custom = System.getProperty(SYSTEM_PROPERTIES_PROP);
368 if (custom != null)
369 {
370 try
371 {
372 propURL = new URL(custom);
373 }
374 catch (MalformedURLException ex)
375 {
376 System.err.print("Main: " + ex);
377 return;
378 }
379 }
380 else
381 {
382 // Determine where the configuration directory is by figuring
383 // out where felix.jar is located on the system class path.
384 File confDir = null;
385 String classpath = System.getProperty("java.class.path");
386 int index = classpath.toLowerCase().indexOf("felix.jar");
387 int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
388 if (index >= start)
389 {
390 // Get the path of the felix.jar file.
391 String jarLocation = classpath.substring(start, index);
392 // Calculate the conf directory based on the parent
393 // directory of the felix.jar directory.
394 confDir = new File(
395 new File(new File(jarLocation).getAbsolutePath()).getParent(),
396 CONFIG_DIRECTORY);
397 }
398 else
399 {
400 // Can't figure it out so use the current directory as default.
401 confDir = new File(System.getProperty("user.dir"), CONFIG_DIRECTORY);
402 }
403
404 try
405 {
406 propURL = new File(confDir, SYSTEM_PROPERTIES_FILE_VALUE).toURL();
407 }
408 catch (MalformedURLException ex)
409 {
410 System.err.print("Main: " + ex);
411 return;
412 }
413 }
414
415 // Read the properties file.
416 Properties props = new Properties();
417 InputStream is = null;
418 try
419 {
420 is = propURL.openConnection().getInputStream();
421 props.load(is);
422 is.close();
423 }
424 catch (FileNotFoundException ex)
425 {
426 // Ignore file not found.
427 }
428 catch (Exception ex)
429 {
430 System.err.println(
431 "Main: Error loading system properties from " + propURL);
432 System.err.println("Main: " + ex);
433 try
434 {
435 if (is != null) is.close();
436 }
437 catch (IOException ex2)
438 {
439 // Nothing we can do.
440 }
441 return;
442 }
443
444 // Perform variable substitution on specified properties.
445 for (Enumeration e = props.propertyNames(); e.hasMoreElements(); )
446 {
447 String name = (String) e.nextElement();
448 System.setProperty(name,
449 Util.substVars(props.getProperty(name), name, null, null));
450 }
451 }
452
453 /**
454 * <p>
455 * Loads the configuration properties in the configuration property file
456 * associated with the framework installation; these properties
457 * are accessible to the framework and to bundles and are intended
458 * for configuration purposes. By default, the configuration property
459 * file is located in the <tt>conf/</tt> directory of the Felix
460 * installation directory and is called "<tt>config.properties</tt>".
461 * The installation directory of Felix is assumed to be the parent
462 * directory of the <tt>felix.jar</tt> file as found on the system class
463 * path property. The precise file from which to load configuration
464 * properties can be set by initializing the "<tt>felix.config.properties</tt>"
465 * system property to an arbitrary URL.
466 * </p>
467 * @return A <tt>Properties</tt> instance or <tt>null</tt> if there was an error.
468 **/
469 public static Properties loadConfigProperties()
470 {
471 // The config properties file is either specified by a system
472 // property or it is in the conf/ directory of the Felix
473 // installation directory. Try to load it from one of these
474 // places.
475
476 // See if the property URL was specified as a property.
477 URL propURL = null;
478 String custom = System.getProperty(CONFIG_PROPERTIES_PROP);
479 if (custom != null)
480 {
481 try
482 {
483 propURL = new URL(custom);
484 }
485 catch (MalformedURLException ex)
486 {
487 System.err.print("Main: " + ex);
488 return null;
489 }
490 }
491 else
492 {
493 // Determine where the configuration directory is by figuring
494 // out where felix.jar is located on the system class path.
495 File confDir = null;
496 String classpath = System.getProperty("java.class.path");
497 int index = classpath.toLowerCase().indexOf("felix.jar");
498 int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
499 if (index >= start)
500 {
501 // Get the path of the felix.jar file.
502 String jarLocation = classpath.substring(start, index);
503 // Calculate the conf directory based on the parent
504 // directory of the felix.jar directory.
505 confDir = new File(
506 new File(new File(jarLocation).getAbsolutePath()).getParent(),
507 CONFIG_DIRECTORY);
508 }
509 else
510 {
511 // Can't figure it out so use the current directory as default.
512 confDir = new File(System.getProperty("user.dir"), CONFIG_DIRECTORY);
513 }
514
515 try
516 {
517 propURL = new File(confDir, CONFIG_PROPERTIES_FILE_VALUE).toURL();
518 }
519 catch (MalformedURLException ex)
520 {
521 System.err.print("Main: " + ex);
522 return null;
523 }
524 }
525
526 // Read the properties file.
527 Properties props = new Properties();
528 InputStream is = null;
529 try
530 {
531 // Try to load config.properties.
532 is = propURL.openConnection().getInputStream();
533 props.load(is);
534 is.close();
535 }
536 catch (Exception ex)
537 {
538 // Try to close input stream if we have one.
539 try
540 {
541 if (is != null) is.close();
542 }
543 catch (IOException ex2)
544 {
545 // Nothing we can do.
546 }
547
548 return null;
549 }
550
551 // Perform variable substitution for system properties.
552 for (Enumeration e = props.propertyNames(); e.hasMoreElements(); )
553 {
554 String name = (String) e.nextElement();
555 props.setProperty(name,
556 Util.substVars(props.getProperty(name), name, null, props));
557 }
558
559 return props;
560 }
561
562 public static void copySystemProperties(Properties configProps)
563 {
564 for (Enumeration e = System.getProperties().propertyNames();
565 e.hasMoreElements(); )
566 {
567 String key = (String) e.nextElement();
568 if (key.startsWith("felix.") || key.startsWith("org.osgi.framework."))
569 {
570 configProps.setProperty(key, System.getProperty(key));
571 }
572 }
573 }
574 }