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