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