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:bs@nuxeo.com">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 sb = new StringBuilder(); 164 for (File file : files) { 165 if (file != null) { 166 sb.append("\n\t").append(file.getPath()); 167 } 168 } 169 log.debug("Deployment order: " + sb.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 } 227 228 private static void doStop() throws BundleException { 229 try { 230 osgi.shutdown(); 231 } catch (IOException e) { 232 throw new BundleException("Cannot shutdown OSGi", e); 233 } 234 } 235 236 public static void uninstall(String symbolicName) throws BundleException { 237 BundleImpl bundle = osgi.getBundle(symbolicName); 238 if (bundle != null) { 239 bundle.uninstall(); 240 } 241 } 242 243 public static String install(File f) throws IOException, BundleException { 244 BundleFile bf; 245 if (f.isDirectory()) { 246 bf = new DirectoryBundleFile(f); 247 } else { 248 bf = new JarBundleFile(f); 249 } 250 BundleImpl bundle = new BundleImpl(osgi, bf, loader); 251 if (bundle.getState() == 0) { 252 // not a bundle (no Bundle-SymbolicName) 253 return null; 254 } 255 osgi.install(bundle); 256 return bundle.getSymbolicName(); 257 } 258 259 public static void preprocess() { 260 File f = new File(home, "OSGI-INF/deployment-container.xml"); 261 if (!f.isFile()) { // make sure a preprocessing container is defined 262 return; 263 } 264 try { 265 Class<?> klass = loader.loadClass("org.nuxeo.runtime.deployment.preprocessor.DeploymentPreprocessor"); 266 Method main = klass.getMethod("main", String[].class); 267 main.invoke(null, new Object[] { new String[] { home.getAbsolutePath() } }); 268 } catch (ReflectiveOperationException e) { 269 throw new RuntimeException(e); 270 } 271 } 272 273 protected static void loadSystemProperties() { 274 File file = new File(home, "system.properties"); 275 if (!file.isFile()) { 276 return; 277 } 278 FileInputStream in = null; 279 try { 280 in = new FileInputStream(file); 281 Properties p = new Properties(); 282 p.load(in); 283 for (Map.Entry<Object, Object> entry : p.entrySet()) { 284 String v = (String) entry.getValue(); 285 v = StringUtils.expandVars(v, System.getProperties()); 286 System.setProperty((String) entry.getKey(), v); 287 } 288 } catch (IOException e) { 289 throw new RuntimeException("Failed to load system properties", e); 290 } finally { 291 if (in != null) { 292 try { 293 in.close(); 294 } catch (IOException e) { 295 log.error(e); 296 } 297 } 298 } 299 } 300 301 protected static String getEnvProperty(String key, Map<String, Object> hostEnv, Properties sysprops, 302 boolean addToSystemProperties) { 303 String v = (String) hostEnv.get(key); 304 if (v == null) { 305 v = System.getProperty(key); 306 } 307 if (v != null) { 308 v = StringUtils.expandVars(v, sysprops); 309 if (addToSystemProperties) { 310 sysprops.setProperty(key, v); 311 } 312 } 313 return v; 314 } 315 316 protected static Environment createEnvironment(File home, Map<String, Object> hostEnv) { 317 Properties sysprops = System.getProperties(); 318 sysprops.setProperty(Environment.NUXEO_RUNTIME_HOME, home.getAbsolutePath()); 319 320 Environment env = Environment.getDefault(); 321 if (env == null) { 322 env = new Environment(home); 323 } 324 if (!home.equals(env.getRuntimeHome())) { 325 env.setRuntimeHome(home); 326 } 327 328 String v = (String) hostEnv.get(HOST_NAME); 329 env.setHostApplicationName(v == null ? Environment.NXSERVER_HOST : v); 330 v = (String) hostEnv.get(HOST_VERSION); 331 if (v != null) { 332 env.setHostApplicationVersion((String) hostEnv.get(HOST_VERSION)); 333 } 334 335 v = getEnvProperty(Environment.NUXEO_DATA_DIR, hostEnv, sysprops, true); 336 if (v != null) { 337 env.setData(new File(v)); 338 } else { 339 sysprops.setProperty(Environment.NUXEO_DATA_DIR, env.getData().getAbsolutePath()); 340 } 341 342 v = getEnvProperty(Environment.NUXEO_LOG_DIR, hostEnv, sysprops, true); 343 if (v != null) { 344 env.setLog(new File(v)); 345 } else { 346 sysprops.setProperty(Environment.NUXEO_LOG_DIR, env.getLog().getAbsolutePath()); 347 } 348 349 v = getEnvProperty(Environment.NUXEO_TMP_DIR, hostEnv, sysprops, true); 350 if (v != null) { 351 env.setTemp(new File(v)); 352 } else { 353 sysprops.setProperty(Environment.NUXEO_TMP_DIR, env.getTemp().getAbsolutePath()); 354 } 355 356 v = getEnvProperty(Environment.NUXEO_CONFIG_DIR, hostEnv, sysprops, true); 357 if (v != null) { 358 env.setConfig(new File(v)); 359 } else { 360 sysprops.setProperty(Environment.NUXEO_CONFIG_DIR, env.getConfig().getAbsolutePath()); 361 } 362 363 v = getEnvProperty(Environment.NUXEO_WEB_DIR, hostEnv, sysprops, true); 364 if (v != null) { 365 env.setWeb(new File(v)); 366 } else { 367 sysprops.setProperty(Environment.NUXEO_WEB_DIR, env.getWeb().getAbsolutePath()); 368 } 369 370 v = (String) hostEnv.get(ARGS); 371 if (v != null) { 372 env.setCommandLineArguments(v.split("\\s+")); 373 } else { 374 env.setCommandLineArguments(new String[0]); 375 } 376 return env; 377 } 378 379 protected static void printStartMessage() { 380 StringBuilder msg = getStartMessage(); 381 log.info(msg); 382 } 383 384 /** 385 * @since 5.5 386 * @return Environment summary 387 */ 388 protected static StringBuilder getStartMessage() { 389 String newline = System.getProperty("line.separator"); 390 Environment env = Environment.getDefault(); 391 String hr = "======================================================================"; 392 StringBuilder msg = new StringBuilder(newline); 393 msg.append(hr).append(newline); 394 msg.append("= Starting Nuxeo Framework").append(newline); 395 msg.append(hr).append(newline); 396 msg.append(" * Server home = ").append(env.getServerHome()).append(newline); 397 msg.append(" * Runtime home = ").append(env.getRuntimeHome()).append(newline); 398 msg.append(" * Data Directory = ").append(env.getData()).append(newline); 399 msg.append(" * Log Directory = ").append(env.getLog()).append(newline); 400 msg.append(" * Configuration Directory = ").append(env.getConfig()).append(newline); 401 msg.append(" * Temp Directory = ").append(env.getTemp()).append(newline); 402 msg.append(hr); 403 return msg; 404 } 405}