/*
* (C) Copyright 2006-2017 Nuxeo (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* bstefanescu, jcarsique
*/
package org.nuxeo.osgi.application.loader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.common.Environment;
import org.nuxeo.common.utils.StringUtils;
import org.nuxeo.osgi.BundleFile;
import org.nuxeo.osgi.BundleImpl;
import org.nuxeo.osgi.DirectoryBundleFile;
import org.nuxeo.osgi.JarBundleFile;
import org.nuxeo.osgi.OSGiAdapter;
import org.nuxeo.osgi.SystemBundle;
import org.nuxeo.osgi.SystemBundleFile;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class FrameworkLoader {
public static final String HOST_NAME = "org.nuxeo.app.host.name";
public static final String HOST_VERSION = "org.nuxeo.app.host.version";
/**
* @deprecated since 5.4.2 prefer use of {@link Environment#NUXEO_TMP_DIR}
*/
@Deprecated
public static final String TMP_DIR = "org.nuxeo.app.tmp";
public static final String LIBS = "org.nuxeo.app.libs"; // class path
public static final String BUNDLES = "org.nuxeo.app.bundles"; // class path
public static final String DEVMODE = "org.nuxeo.app.devmode";
public static final String PREPROCESSING = "org.nuxeo.app.preprocessing";
public static final String SCAN_FOR_NESTED_JARS = "org.nuxeo.app.scanForNestedJars";
public static final String FLUSH_CACHE = "org.nuxeo.app.flushCache";
public static final String ARGS = "org.nuxeo.app.args";
private static final Log log = LogFactory.getLog(FrameworkLoader.class);
private static boolean isInitialized;
private static boolean isStarted;
private static File home;
private static ClassLoader loader;
private static List<File> bundleFiles;
private static OSGiAdapter osgi;
public static OSGiAdapter osgi() {
return osgi;
}
public static ClassLoader getLoader() {
return loader;
}
public static synchronized void initialize(ClassLoader cl, File home, List<File> bundleFiles,
Map<String, Object> hostEnv) {
if (isInitialized) {
return;
}
FrameworkLoader.home = home;
FrameworkLoader.bundleFiles = bundleFiles == null ? new ArrayList<>() : bundleFiles;
Collections.sort(FrameworkLoader.bundleFiles);
loader = cl;
doInitialize(hostEnv);
osgi = new OSGiAdapter(home);
isInitialized = true;
}
public static synchronized void start() throws BundleException {
if (isStarted) {
return;
}
if (!isInitialized) {
throw new IllegalStateException("Framework is not initialized. Call initialize method first");
}
try {
doStart();
} finally {
isStarted = true;
}
}
public static synchronized void stop() throws BundleException {
if (!isStarted) {
return;
}
try {
doStop();
} finally {
isStarted = false;
}
}
private static void doInitialize(Map<String, Object> hostEnv) {
boolean doPreprocessing = true;
String v = (String) hostEnv.get(PREPROCESSING);
if (v != null) {
doPreprocessing = Boolean.parseBoolean(v);
}
// build environment
Environment env = createEnvironment(home, hostEnv);
Environment.setDefault(env);
loadSystemProperties();
// start bundle pre-processing if requested
if (doPreprocessing) {
try {
preprocess();
} catch (RuntimeException e) {
throw new RuntimeException("Failed to run preprocessing", e);
}
}
}
protected static void printDeploymentOrderInfo(List<File> files) {
if (log.isDebugEnabled()) {
StringBuilder buf = new StringBuilder();
for (File file : files) {
if (file != null) {
buf.append("\n\t").append(file.getPath());
}
}
log.debug("Deployment order: " + buf.toString());
}
}
protected static Attributes.Name SYMBOLIC_NAME = new Attributes.Name(Constants.BUNDLE_SYMBOLICNAME);
protected static boolean isBundle(File f) {
Manifest mf;
try {
if (f.isFile()) { // jar file
try (JarFile jf = new JarFile(f)) {
mf = jf.getManifest();
}
if (mf == null) {
return false;
}
} else if (f.isDirectory()) { // directory
f = new File(f, "META-INF/MANIFEST.MF");
if (!f.isFile()) {
return false;
}
mf = new Manifest();
try (FileInputStream input = new FileInputStream(f)) {
mf.read(input);
}
} else {
return false;
}
} catch (IOException e) {
return false;
}
return mf.getMainAttributes().containsKey(SYMBOLIC_NAME);
}
private static void doStart() throws BundleException {
printStartMessage();
// install system bundle first
BundleFile bf;
try {
bf = new SystemBundleFile(home);
} catch (IOException e) {
throw new BundleException("Cannot create system bundle for " + home, e);
}
SystemBundle systemBundle = new SystemBundle(osgi, bf, loader);
osgi.setSystemBundle(systemBundle);
printDeploymentOrderInfo(bundleFiles);
for (File f : bundleFiles) {
if (!isBundle(f)) {
continue;
}
try {
install(f);
} catch (IOException | BundleException | RuntimeException e) {
log.error("Failed to install bundle: " + f, e);
}
}
osgi.fireFrameworkEvent(new FrameworkEvent(FrameworkEvent.STARTED, systemBundle, null));
// osgi.fireFrameworkEvent(new
// FrameworkEvent(FrameworkEvent.AFTER_START, systemBundle, null));
}
private static void doStop() throws BundleException {
try {
osgi.shutdown();
} catch (IOException e) {
throw new BundleException("Cannot shutdown OSGi", e);
}
}
public static void uninstall(String symbolicName) throws BundleException {
BundleImpl bundle = osgi.getBundle(symbolicName);
if (bundle != null) {
bundle.uninstall();
}
}
public static String install(File f) throws IOException, BundleException {
BundleFile bf;
if (f.isDirectory()) {
bf = new DirectoryBundleFile(f);
} else {
bf = new JarBundleFile(f);
}
BundleImpl bundle = new BundleImpl(osgi, bf, loader);
if (bundle.getState() == 0) {
// not a bundle (no Bundle-SymbolicName)
return null;
}
osgi.install(bundle);
return bundle.getSymbolicName();
}
public static void preprocess() {
File f = new File(home, "OSGI-INF/deployment-container.xml");
if (!f.isFile()) { // make sure a preprocessing container is defined
return;
}
try {
Class<?> klass = loader.loadClass("org.nuxeo.runtime.deployment.preprocessor.DeploymentPreprocessor");
Method main = klass.getMethod("main", String[].class);
main.invoke(null, new Object[] { new String[] { home.getAbsolutePath() } });
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
protected static void loadSystemProperties() {
File file = new File(home, "system.properties");
if (!file.isFile()) {
return;
}
FileInputStream in = null;
try {
in = new FileInputStream(file);
Properties p = new Properties();
p.load(in);
for (Map.Entry<Object, Object> entry : p.entrySet()) {
String v = (String) entry.getValue();
v = StringUtils.expandVars(v, System.getProperties());
System.setProperty((String) entry.getKey(), v);
}
} catch (IOException e) {
throw new RuntimeException("Failed to load system properties", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error(e);
}
}
}
}
protected static String getEnvProperty(String key, Map<String, Object> hostEnv, Properties sysprops,
boolean addToSystemProperties) {
String v = (String) hostEnv.get(key);
if (v == null) {
v = System.getProperty(key);
}
if (v != null) {
v = StringUtils.expandVars(v, sysprops);
if (addToSystemProperties) {
sysprops.setProperty(key, v);
}
}
return v;
}
protected static Environment createEnvironment(File home, Map<String, Object> hostEnv) {
Properties sysprops = System.getProperties();
sysprops.setProperty(Environment.NUXEO_RUNTIME_HOME, home.getAbsolutePath());
Environment env = Environment.getDefault();
if (env == null) {
env = new Environment(home);
}
if (!home.equals(env.getRuntimeHome())) {
env.setRuntimeHome(home);
}
String v = (String) hostEnv.get(HOST_NAME);
env.setHostApplicationName(v == null ? Environment.NXSERVER_HOST : v);
v = (String) hostEnv.get(HOST_VERSION);
if (v != null) {
env.setHostApplicationVersion((String) hostEnv.get(HOST_VERSION));
}
v = getEnvProperty(Environment.NUXEO_DATA_DIR, hostEnv, sysprops, true);
if (v != null) {
env.setData(new File(v));
} else {
sysprops.setProperty(Environment.NUXEO_DATA_DIR, env.getData().getAbsolutePath());
}
v = getEnvProperty(Environment.NUXEO_LOG_DIR, hostEnv, sysprops, true);
if (v != null) {
env.setLog(new File(v));
} else {
sysprops.setProperty(Environment.NUXEO_LOG_DIR, env.getLog().getAbsolutePath());
}
v = getEnvProperty(Environment.NUXEO_TMP_DIR, hostEnv, sysprops, true);
if (v != null) {
env.setTemp(new File(v));
} else {
sysprops.setProperty(Environment.NUXEO_TMP_DIR, env.getTemp().getAbsolutePath());
}
v = getEnvProperty(Environment.NUXEO_CONFIG_DIR, hostEnv, sysprops, true);
if (v != null) {
env.setConfig(new File(v));
} else {
sysprops.setProperty(Environment.NUXEO_CONFIG_DIR, env.getConfig().getAbsolutePath());
}
v = getEnvProperty(Environment.NUXEO_WEB_DIR, hostEnv, sysprops, true);
if (v != null) {
env.setWeb(new File(v));
} else {
sysprops.setProperty(Environment.NUXEO_WEB_DIR, env.getWeb().getAbsolutePath());
}
v = (String) hostEnv.get(ARGS);
if (v != null) {
env.setCommandLineArguments(v.split("\\s+"));
} else {
env.setCommandLineArguments(new String[0]);
}
return env;
}
protected static void printStartMessage() {
StringBuilder msg = getStartMessage();
log.info(msg);
}
/**
* @since 5.5
* @return Environment summary
*/
protected static StringBuilder getStartMessage() {
String newline = System.getProperty("line.separator");
Environment env = Environment.getDefault();
String hr = "======================================================================";
StringBuilder msg = new StringBuilder(newline);
msg.append(hr).append(newline);
msg.append("= Starting Nuxeo Framework").append(newline);
msg.append(hr).append(newline);
msg.append(" * Server home = ").append(env.getServerHome()).append(newline);
msg.append(" * Runtime home = ").append(env.getRuntimeHome()).append(newline);
msg.append(" * Data Directory = ").append(env.getData()).append(newline);
msg.append(" * Log Directory = ").append(env.getLog()).append(newline);
msg.append(" * Configuration Directory = ").append(env.getConfig()).append(newline);
msg.append(" * Temp Directory = ").append(env.getTemp()).append(newline);
// System.out.println(" * System Bundle = "+systemBundle);
// System.out.println(" * Command Line Args = "+Arrays.asList(env.getCommandLineArguments()));
msg.append(hr);
return msg;
}
}