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 }