001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 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<>() : 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 124 try { 125 doStart(); 126 } finally { 127 isStarted = true; 128 } 129 } 130 131 public static synchronized void stop() throws BundleException { 132 if (!isStarted) { 133 return; 134 } 135 try { 136 doStop(); 137 } finally { 138 isStarted = false; 139 } 140 } 141 142 private static void doInitialize(Map<String, Object> hostEnv) { 143 // make sure this property was correctly initialized 144 System.setProperty(Environment.HOME_DIR, home.getAbsolutePath()); 145 boolean doPreprocessing = true; 146 String v = (String) hostEnv.get(PREPROCESSING); 147 if (v != null) { 148 doPreprocessing = Boolean.parseBoolean(v); 149 } 150 // build environment 151 Environment env = createEnvironment(home, hostEnv); 152 Environment.setDefault(env); 153 loadSystemProperties(); 154 // start bundle pre-processing if requested 155 if (doPreprocessing) { 156 try { 157 preprocess(); 158 } catch (RuntimeException e) { 159 throw new RuntimeException("Failed to run preprocessing", e); 160 } 161 } 162 } 163 164 protected static void printDeploymentOrderInfo(List<File> files) { 165 if (log.isDebugEnabled()) { 166 StringBuilder buf = new StringBuilder(); 167 for (File file : files) { 168 if (file != null) { 169 buf.append("\n\t" + file.getPath()); 170 } 171 } 172 log.debug("Deployment order: " + buf.toString()); 173 } 174 } 175 176 protected static Attributes.Name SYMBOLIC_NAME = new Attributes.Name(Constants.BUNDLE_SYMBOLICNAME); 177 178 protected static boolean isBundle(File f) { 179 Manifest mf; 180 try { 181 if (f.isFile()) { // jar file 182 JarFile jf = new JarFile(f); 183 try { 184 mf = jf.getManifest(); 185 } finally { 186 jf.close(); 187 } 188 if (mf == null) { 189 return false; 190 } 191 } else if (f.isDirectory()) { // directory 192 f = new File(f, "META-INF/MANIFEST.MF"); 193 if (!f.isFile()) { 194 return false; 195 } 196 mf = new Manifest(); 197 FileInputStream input = new FileInputStream(f); 198 try { 199 mf.read(input); 200 } finally { 201 input.close(); 202 } 203 } else { 204 return false; 205 } 206 } catch (IOException e) { 207 return false; 208 } 209 return mf.getMainAttributes().containsKey(SYMBOLIC_NAME); 210 } 211 212 private static void doStart() throws BundleException { 213 printStartMessage(); 214 // install system bundle first 215 BundleFile bf; 216 try { 217 bf = new SystemBundleFile(home); 218 } catch (IOException e) { 219 throw new BundleException("Cannot create system bundle for " + home, e); 220 } 221 SystemBundle systemBundle = new SystemBundle(osgi, bf, loader); 222 osgi.setSystemBundle(systemBundle); 223 printDeploymentOrderInfo(bundleFiles); 224 for (File f : bundleFiles) { 225 if (!isBundle(f)) { 226 continue; 227 } 228 try { 229 install(f); 230 } catch (IOException e) { 231 log.error("Failed to install bundle: " + f, e); 232 // continue 233 } catch (BundleException e) { 234 log.error("Failed to install bundle: " + f, e); 235 // continue 236 } catch (RuntimeException e) { 237 log.error("Failed to install bundle: " + f, e); 238 // continue 239 } 240 } 241 osgi.fireFrameworkEvent(new FrameworkEvent(FrameworkEvent.STARTED, systemBundle, null)); 242 // osgi.fireFrameworkEvent(new 243 // FrameworkEvent(FrameworkEvent.AFTER_START, systemBundle, null)); 244 } 245 246 private static void doStop() throws BundleException { 247 try { 248 osgi.shutdown(); 249 } catch (IOException e) { 250 throw new BundleException("Cannot shutdown OSGi", e); 251 } 252 } 253 254 public static void uninstall(String symbolicName) throws BundleException { 255 BundleImpl bundle = osgi.getBundle(symbolicName); 256 if (bundle != null) { 257 bundle.uninstall(); 258 } 259 } 260 261 public static String install(File f) throws IOException, BundleException { 262 BundleFile bf = null; 263 if (f.isDirectory()) { 264 bf = new DirectoryBundleFile(f); 265 } else { 266 bf = new JarBundleFile(f); 267 } 268 BundleImpl bundle = new BundleImpl(osgi, bf, loader); 269 if (bundle.getState() == 0) { 270 // not a bundle (no Bundle-SymbolicName) 271 return null; 272 } 273 osgi.install(bundle); 274 return bundle.getSymbolicName(); 275 } 276 277 public static void preprocess() { 278 File f = new File(home, "OSGI-INF/deployment-container.xml"); 279 if (!f.isFile()) { // make sure a preprocessing container is defined 280 return; 281 } 282 try { 283 Class<?> klass = loader.loadClass("org.nuxeo.runtime.deployment.preprocessor.DeploymentPreprocessor"); 284 Method main = klass.getMethod("main", String[].class); 285 main.invoke(null, new Object[] { new String[] { home.getAbsolutePath() } }); 286 } catch (ClassNotFoundException e) { 287 throw new RuntimeException(e); 288 } catch (SecurityException e) { 289 throw new RuntimeException(e); 290 } catch (NoSuchMethodException e) { 291 throw new RuntimeException(e); 292 } catch (IllegalAccessException e) { 293 throw new RuntimeException(e); 294 } catch (InvocationTargetException e) { 295 throw new RuntimeException(e); 296 } 297 } 298 299 protected static void loadSystemProperties() { 300 System.setProperty(Environment.HOME_DIR, home.getAbsolutePath()); 301 File file = new File(home, "system.properties"); 302 if (!file.isFile()) { 303 return; 304 } 305 FileInputStream in = null; 306 try { 307 in = new FileInputStream(file); 308 Properties p = new Properties(); 309 p.load(in); 310 for (Map.Entry<Object, Object> entry : p.entrySet()) { 311 String v = (String) entry.getValue(); 312 v = StringUtils.expandVars(v, System.getProperties()); 313 System.setProperty((String) entry.getKey(), v); 314 } 315 } catch (IOException e) { 316 throw new RuntimeException("Failed to load system properties", e); 317 } finally { 318 if (in != null) { 319 try { 320 in.close(); 321 } catch (IOException e) { 322 log.error(e); 323 } 324 } 325 } 326 } 327 328 protected static String getEnvProperty(String key, Map<String, Object> hostEnv, Properties sysprops, 329 boolean addToSystemProperties) { 330 String v = (String) hostEnv.get(key); 331 if (v == null) { 332 v = System.getProperty(key); 333 } 334 if (v != null) { 335 v = StringUtils.expandVars(v, sysprops); 336 if (addToSystemProperties) { 337 sysprops.setProperty(key, v); 338 } 339 } 340 return v; 341 } 342 343 protected static Environment createEnvironment(File home, Map<String, Object> hostEnv) { 344 Properties sysprops = System.getProperties(); 345 sysprops.setProperty(Environment.NUXEO_RUNTIME_HOME, home.getAbsolutePath()); 346 347 Environment env = Environment.getDefault(); 348 if (env == null) { 349 env = new Environment(home); 350 } 351 if (!home.equals(env.getRuntimeHome())) { 352 env.setRuntimeHome(home); 353 } 354 355 String v = (String) hostEnv.get(HOST_NAME); 356 env.setHostApplicationName(v == null ? Environment.NXSERVER_HOST : v); 357 v = (String) hostEnv.get(HOST_VERSION); 358 if (v != null) { 359 env.setHostApplicationVersion((String) hostEnv.get(HOST_VERSION)); 360 } 361 362 v = getEnvProperty(Environment.NUXEO_DATA_DIR, hostEnv, sysprops, true); 363 if (v != null) { 364 env.setData(new File(v)); 365 } else { 366 sysprops.setProperty(Environment.NUXEO_DATA_DIR, env.getData().getAbsolutePath()); 367 } 368 369 v = getEnvProperty(Environment.NUXEO_LOG_DIR, hostEnv, sysprops, true); 370 if (v != null) { 371 env.setLog(new File(v)); 372 } else { 373 sysprops.setProperty(Environment.NUXEO_LOG_DIR, env.getLog().getAbsolutePath()); 374 } 375 376 v = getEnvProperty(Environment.NUXEO_TMP_DIR, hostEnv, sysprops, true); 377 if (v != null) { 378 env.setTemp(new File(v)); 379 } else { 380 sysprops.setProperty(Environment.NUXEO_TMP_DIR, env.getTemp().getAbsolutePath()); 381 } 382 383 v = getEnvProperty(Environment.NUXEO_CONFIG_DIR, hostEnv, sysprops, true); 384 if (v != null) { 385 env.setConfig(new File(v)); 386 } else { 387 sysprops.setProperty(Environment.NUXEO_CONFIG_DIR, env.getConfig().getAbsolutePath()); 388 } 389 390 v = getEnvProperty(Environment.NUXEO_WEB_DIR, hostEnv, sysprops, true); 391 if (v != null) { 392 env.setWeb(new File(v)); 393 } else { 394 sysprops.setProperty(Environment.NUXEO_WEB_DIR, env.getWeb().getAbsolutePath()); 395 } 396 397 v = (String) hostEnv.get(ARGS); 398 if (v != null) { 399 env.setCommandLineArguments(v.split("\\s+")); 400 } else { 401 env.setCommandLineArguments(new String[0]); 402 } 403 return env; 404 } 405 406 protected static void printStartMessage() { 407 StringBuilder msg = getStartMessage(); 408 log.info(msg); 409 } 410 411 /** 412 * @since 5.5 413 * @return Environment summary 414 */ 415 protected static StringBuilder getStartMessage() { 416 String newline = System.getProperty("line.separator"); 417 Environment env = Environment.getDefault(); 418 String hr = "======================================================================"; 419 StringBuilder msg = new StringBuilder(newline); 420 msg.append(hr + newline); 421 msg.append("= Starting Nuxeo Framework" + newline); 422 msg.append(hr + newline); 423 msg.append(" * Server home = " + env.getServerHome() + newline); 424 msg.append(" * Runtime home = " + env.getRuntimeHome() + newline); 425 msg.append(" * Data Directory = " + env.getData() + newline); 426 msg.append(" * Log Directory = " + env.getLog() + newline); 427 msg.append(" * Configuration Directory = " + env.getConfig() + newline); 428 msg.append(" * Temp Directory = " + env.getTemp() + newline); 429 // System.out.println(" * System Bundle = "+systemBundle); 430 // System.out.println(" * Command Line Args = "+Arrays.asList(env.getCommandLineArguments())); 431 msg.append(hr); 432 return msg; 433 } 434}