001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu, jcarsique 018 */ 019package org.nuxeo.osgi.application.loader; 020 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.IOException; 024import java.lang.reflect.InvocationTargetException; 025import java.lang.reflect.Method; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029import java.util.Map; 030import java.util.Properties; 031import java.util.jar.Attributes; 032import java.util.jar.JarFile; 033import java.util.jar.Manifest; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.nuxeo.common.Environment; 038import org.nuxeo.common.utils.StringUtils; 039import org.nuxeo.osgi.BundleFile; 040import org.nuxeo.osgi.BundleImpl; 041import org.nuxeo.osgi.DirectoryBundleFile; 042import org.nuxeo.osgi.JarBundleFile; 043import org.nuxeo.osgi.OSGiAdapter; 044import org.nuxeo.osgi.SystemBundle; 045import org.nuxeo.osgi.SystemBundleFile; 046import org.osgi.framework.BundleException; 047import org.osgi.framework.Constants; 048import org.osgi.framework.FrameworkEvent; 049 050/** 051 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 052 */ 053public class FrameworkLoader { 054 055 public static final String HOST_NAME = "org.nuxeo.app.host.name"; 056 057 public static final String HOST_VERSION = "org.nuxeo.app.host.version"; 058 059 /** 060 * @deprecated prefer use of {@link Environment#NUXEO_TMP_DIR} 061 */ 062 @Deprecated 063 public static final String TMP_DIR = "org.nuxeo.app.tmp"; 064 065 public static final String LIBS = "org.nuxeo.app.libs"; // class path 066 067 public static final String BUNDLES = "org.nuxeo.app.bundles"; // class path 068 069 public static final String DEVMODE = "org.nuxeo.app.devmode"; 070 071 public static final String PREPROCESSING = "org.nuxeo.app.preprocessing"; 072 073 public static final String SCAN_FOR_NESTED_JARS = "org.nuxeo.app.scanForNestedJars"; 074 075 public static final String FLUSH_CACHE = "org.nuxeo.app.flushCache"; 076 077 public static final String ARGS = "org.nuxeo.app.args"; 078 079 private static final Log log = LogFactory.getLog(FrameworkLoader.class); 080 081 private static boolean isInitialized; 082 083 private static boolean isStarted; 084 085 private static File home; 086 087 private static ClassLoader loader; 088 089 private static List<File> bundleFiles; 090 091 private static OSGiAdapter osgi; 092 093 public static OSGiAdapter osgi() { 094 return osgi; 095 } 096 097 public static ClassLoader getLoader() { 098 return loader; 099 } 100 101 public static synchronized void initialize(ClassLoader cl, File home, List<File> bundleFiles, 102 Map<String, Object> hostEnv) { 103 if (isInitialized) { 104 return; 105 } 106 FrameworkLoader.home = home; 107 FrameworkLoader.bundleFiles = bundleFiles == null ? new ArrayList<File>() : bundleFiles; 108 Collections.sort(FrameworkLoader.bundleFiles); 109 110 loader = cl; 111 doInitialize(hostEnv); 112 osgi = new OSGiAdapter(home); 113 isInitialized = true; 114 } 115 116 public static synchronized void start() throws BundleException { 117 if (isStarted) { 118 return; 119 } 120 if (!isInitialized) { 121 throw new IllegalStateException("Framework is not initialized. Call initialize method first"); 122 } 123 ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); 124 try { 125 Thread.currentThread().setContextClassLoader(loader); 126 doStart(); 127 } finally { 128 Thread.currentThread().setContextClassLoader(oldCl); 129 } 130 isStarted = true; 131 } 132 133 public static synchronized void stop() throws BundleException { 134 if (!isStarted) { 135 return; 136 } 137 ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); 138 try { 139 Thread.currentThread().setContextClassLoader(loader); 140 doStop(); 141 } finally { 142 Thread.currentThread().setContextClassLoader(oldCl); 143 } 144 isStarted = false; 145 } 146 147 private static void doInitialize(Map<String, Object> hostEnv) { 148 // make sure this property was correctly initialized 149 System.setProperty(Environment.HOME_DIR, home.getAbsolutePath()); 150 boolean doPreprocessing = true; 151 String v = (String) hostEnv.get(PREPROCESSING); 152 if (v != null) { 153 doPreprocessing = Boolean.parseBoolean(v); 154 } 155 // build environment 156 Environment env = createEnvironment(home, hostEnv); 157 Environment.setDefault(env); 158 loadSystemProperties(); 159 // start bundle pre-processing if requested 160 if (doPreprocessing) { 161 try { 162 preprocess(); 163 } catch (RuntimeException e) { 164 throw new RuntimeException("Failed to run preprocessing", e); 165 } 166 } 167 } 168 169 protected static void printDeploymentOrderInfo(List<File> files) { 170 if (log.isDebugEnabled()) { 171 StringBuilder buf = new StringBuilder(); 172 for (File file : files) { 173 if (file != null) { 174 buf.append("\n\t" + file.getPath()); 175 } 176 } 177 log.debug("Deployment order: " + buf.toString()); 178 } 179 } 180 181 protected static Attributes.Name SYMBOLIC_NAME = new Attributes.Name(Constants.BUNDLE_SYMBOLICNAME); 182 183 protected static boolean isBundle(File f) { 184 Manifest mf; 185 try { 186 if (f.isFile()) { // jar file 187 JarFile jf = new JarFile(f); 188 try { 189 mf = jf.getManifest(); 190 } finally { 191 jf.close(); 192 } 193 if (mf == null) { 194 return false; 195 } 196 } else if (f.isDirectory()) { // directory 197 f = new File(f, "META-INF/MANIFEST.MF"); 198 if (!f.isFile()) { 199 return false; 200 } 201 mf = new Manifest(); 202 FileInputStream input = new FileInputStream(f); 203 try { 204 mf.read(input); 205 } finally { 206 input.close(); 207 } 208 } else { 209 return false; 210 } 211 } catch (IOException e) { 212 return false; 213 } 214 return mf.getMainAttributes().containsKey(SYMBOLIC_NAME); 215 } 216 217 private static void doStart() throws BundleException { 218 printStartMessage(); 219 // install system bundle first 220 BundleFile bf; 221 try { 222 bf = new SystemBundleFile(home); 223 } catch (IOException e) { 224 throw new BundleException("Cannot create system bundle for " + home, e); 225 } 226 SystemBundle systemBundle = new SystemBundle(osgi, bf, loader); 227 osgi.setSystemBundle(systemBundle); 228 printDeploymentOrderInfo(bundleFiles); 229 for (File f : bundleFiles) { 230 if (!isBundle(f)) { 231 continue; 232 } 233 try { 234 install(f); 235 } catch (IOException e) { 236 log.error("Failed to install bundle: " + f, e); 237 // continue 238 } catch (BundleException e) { 239 log.error("Failed to install bundle: " + f, e); 240 // continue 241 } catch (RuntimeException e) { 242 log.error("Failed to install bundle: " + f, e); 243 // continue 244 } 245 } 246 osgi.fireFrameworkEvent(new FrameworkEvent(FrameworkEvent.STARTED, systemBundle, null)); 247 // osgi.fireFrameworkEvent(new 248 // FrameworkEvent(FrameworkEvent.AFTER_START, systemBundle, null)); 249 } 250 251 private static void doStop() throws BundleException { 252 try { 253 osgi.shutdown(); 254 } catch (IOException e) { 255 throw new BundleException("Cannot shutdown OSGi", e); 256 } 257 } 258 259 public static void uninstall(String symbolicName) throws BundleException { 260 BundleImpl bundle = osgi.getBundle(symbolicName); 261 if (bundle != null) { 262 bundle.uninstall(); 263 } 264 } 265 266 public static String install(File f) throws IOException, BundleException { 267 BundleFile bf = null; 268 if (f.isDirectory()) { 269 bf = new DirectoryBundleFile(f); 270 } else { 271 bf = new JarBundleFile(f); 272 } 273 BundleImpl bundle = new BundleImpl(osgi, bf, loader); 274 if (bundle.getState() == 0) { 275 // not a bundle (no Bundle-SymbolicName) 276 return null; 277 } 278 osgi.install(bundle); 279 return bundle.getSymbolicName(); 280 } 281 282 public static void preprocess() { 283 File f = new File(home, "OSGI-INF/deployment-container.xml"); 284 if (!f.isFile()) { // make sure a preprocessing container is defined 285 return; 286 } 287 try { 288 Class<?> klass = loader.loadClass("org.nuxeo.runtime.deployment.preprocessor.DeploymentPreprocessor"); 289 Method main = klass.getMethod("main", String[].class); 290 main.invoke(null, new Object[] { new String[] { home.getAbsolutePath() } }); 291 } catch (ClassNotFoundException e) { 292 throw new RuntimeException(e); 293 } catch (SecurityException e) { 294 throw new RuntimeException(e); 295 } catch (NoSuchMethodException e) { 296 throw new RuntimeException(e); 297 } catch (IllegalAccessException e) { 298 throw new RuntimeException(e); 299 } catch (InvocationTargetException e) { 300 throw new RuntimeException(e); 301 } 302 } 303 304 protected static void loadSystemProperties() { 305 System.setProperty(Environment.HOME_DIR, home.getAbsolutePath()); 306 File file = new File(home, "system.properties"); 307 if (!file.isFile()) { 308 return; 309 } 310 FileInputStream in = null; 311 try { 312 in = new FileInputStream(file); 313 Properties p = new Properties(); 314 p.load(in); 315 for (Map.Entry<Object, Object> entry : p.entrySet()) { 316 String v = (String) entry.getValue(); 317 v = StringUtils.expandVars(v, System.getProperties()); 318 System.setProperty((String) entry.getKey(), v); 319 } 320 } catch (IOException e) { 321 throw new RuntimeException("Failed to load system properties", e); 322 } finally { 323 if (in != null) { 324 try { 325 in.close(); 326 } catch (IOException e) { 327 log.error(e); 328 } 329 } 330 } 331 } 332 333 protected static String getEnvProperty(String key, Map<String, Object> hostEnv, Properties sysprops, 334 boolean addToSystemProperties) { 335 String v = (String) hostEnv.get(key); 336 if (v == null) { 337 v = System.getProperty(key); 338 } 339 if (v != null) { 340 v = StringUtils.expandVars(v, sysprops); 341 if (addToSystemProperties) { 342 sysprops.setProperty(key, v); 343 } 344 } 345 return v; 346 } 347 348 protected static Environment createEnvironment(File home, Map<String, Object> hostEnv) { 349 Properties sysprops = System.getProperties(); 350 sysprops.setProperty(Environment.NUXEO_RUNTIME_HOME, home.getAbsolutePath()); 351 352 Environment env = new Environment(home); 353 String v = (String) hostEnv.get(HOST_NAME); 354 env.setHostApplicationName(v == null ? Environment.NXSERVER_HOST : v); 355 v = (String) hostEnv.get(HOST_VERSION); 356 if (v != null) { 357 env.setHostApplicationVersion((String) hostEnv.get(HOST_VERSION)); 358 } 359 360 v = getEnvProperty(Environment.NUXEO_DATA_DIR, hostEnv, sysprops, true); 361 if (v != null) { 362 env.setData(new File(v)); 363 } else { 364 sysprops.setProperty(Environment.NUXEO_DATA_DIR, env.getData().getAbsolutePath()); 365 } 366 367 v = getEnvProperty(Environment.NUXEO_LOG_DIR, hostEnv, sysprops, true); 368 if (v != null) { 369 env.setLog(new File(v)); 370 } else { 371 sysprops.setProperty(Environment.NUXEO_LOG_DIR, env.getLog().getAbsolutePath()); 372 } 373 374 v = getEnvProperty(Environment.NUXEO_TMP_DIR, hostEnv, sysprops, true); 375 if (v != null) { 376 env.setTemp(new File(v)); 377 } else { 378 sysprops.setProperty(Environment.NUXEO_TMP_DIR, env.getTemp().getAbsolutePath()); 379 } 380 381 v = getEnvProperty(Environment.NUXEO_CONFIG_DIR, hostEnv, sysprops, true); 382 if (v != null) { 383 env.setConfig(new File(v)); 384 } else { 385 sysprops.setProperty(Environment.NUXEO_CONFIG_DIR, env.getConfig().getAbsolutePath()); 386 } 387 388 v = getEnvProperty(Environment.NUXEO_WEB_DIR, hostEnv, sysprops, true); 389 if (v != null) { 390 env.setWeb(new File(v)); 391 } else { 392 sysprops.setProperty(Environment.NUXEO_WEB_DIR, env.getWeb().getAbsolutePath()); 393 } 394 395 v = (String) hostEnv.get(ARGS); 396 if (v != null) { 397 env.setCommandLineArguments(v.split("\\s+")); 398 } else { 399 env.setCommandLineArguments(new String[0]); 400 } 401 env.getData().mkdirs(); 402 env.getLog().mkdirs(); 403 env.getTemp().mkdirs(); 404 405 return env; 406 } 407 408 protected static void printStartMessage() { 409 StringBuilder msg = getStartMessage(); 410 log.info(msg); 411 } 412 413 /** 414 * @since 5.5 415 * @return Environment summary 416 */ 417 protected static StringBuilder getStartMessage() { 418 String newline = System.getProperty("line.separator"); 419 Environment env = Environment.getDefault(); 420 String hr = "======================================================================"; 421 StringBuilder msg = new StringBuilder(newline); 422 msg.append(hr + newline); 423 msg.append("= Starting Nuxeo Framework" + newline); 424 msg.append(hr + newline); 425 msg.append(" * Server home = " + env.getServerHome() + newline); 426 msg.append(" * Runtime home = " + env.getRuntimeHome() + newline); 427 msg.append(" * Data Directory = " + env.getData() + newline); 428 msg.append(" * Log Directory = " + env.getLog() + newline); 429 msg.append(" * Configuration Directory = " + env.getConfig() + newline); 430 msg.append(" * Temp Directory = " + env.getTemp() + newline); 431 // System.out.println(" * System Bundle = "+systemBundle); 432 // System.out.println(" * Command Line Args = "+Arrays.asList(env.getCommandLineArguments())); 433 msg.append(hr); 434 return msg; 435 } 436}