blob: 2d52b2eac113b6e1bd5a8c89e1c5e54ba34705d4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.sling.crankstart.launcher;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.sling.provisioning.model.Model;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Setup the OSGi framework based on a provisioning model */
@SuppressWarnings("serial")
public class FrameworkSetup extends HashMap<String, Object> implements Callable<Object> {
private final Logger log = LoggerFactory.getLogger(getClass());
@SuppressWarnings("unchecked")
private <T> T require(String key, Class<T> desiredType) throws IllegalStateException {
final Object o = get(key);
if(o == null) {
throw new IllegalStateException("Missing required object:" + key);
}
if(!o.getClass().isAssignableFrom(desiredType)) {
throw new ClassCastException("Object '" + key + "' is not a " + desiredType.getName());
}
return (T)o;
}
public Object call() throws Exception {
final Model model = require(Launcher.MODEL_KEY, Model.class);
final LauncherListener listener = (LauncherListener) get(Launcher.LISTENER_KEY);
log.info("Setting OSGi framework properties");
final Map<String, String> fprops = new FrameworkProperties(model).getProperties(null);
log.info("Starting the OSGi framework");
final FrameworkFactory factory = (FrameworkFactory)getClass().getClassLoader().loadClass("org.apache.felix.framework.FrameworkFactory").newInstance();
final Framework framework = factory.newFramework(fprops);
framework.start();
final RunModeFilter rmFilter = new RunModeFilter();
final Configurations cfg = new Configurations(framework.getBundleContext(), model, rmFilter);
setShutdownHook(framework, new Closeable[] { cfg });
log.info("OSGi framework started");
log.info("Installing bundles from provisioning model");
final BundlesInstaller bi = new BundlesInstaller(model, rmFilter);
final BundleContext bc = framework.getBundleContext();
bi.installBundles(bc, Launcher.NOT_CRANKSTART_FILTER);
cfg.maybeConfigure();
// TODO shall we gradually increase start levels like the launchpad does?? Reuse that DefaultStartupHandler code?
final Bundle [] bundles = bc.getBundles();
log.info("Starting all bundles ({} bundles installed)", bundles.length);
int started = 0;
int failed = 0;
for(Bundle b : bundles) {
if(isFragment(b)) {
started++;
} else {
try {
b.start();
started++;
} catch(BundleException be) {
failed++;
log.warn("Error starting bundle " + b.getSymbolicName(), be);
}
}
cfg.maybeConfigure();
}
if(failed == 0) {
log.info("All {} bundles started.", started);
} else {
log.info("{} bundles started, {} failed to start, total {}", started, failed, bundles.length);
}
log.info("OSGi setup done, waiting for framework to stop");
if ( listener != null) {
listener.onStartup(started, failed, bundles.length);
}
framework.waitForStop(0);
if ( listener != null) {
listener.onShutdown();
}
return null;
}
private boolean isFragment(Bundle b) {
return b.getHeaders().get("Fragment-Host") != null;
}
private void setShutdownHook(final Framework osgiFramework, final Closeable ... toClose) {
// Shutdown the framework when the JVM exits
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if(osgiFramework != null && osgiFramework.getState() == Bundle.ACTIVE) {
try {
log.info("Stopping the OSGi framework");
osgiFramework.stop();
log.info("Waiting for the OSGi framework to exit");
osgiFramework.waitForStop(0);
log.info("OSGi framework stopped");
} catch(Exception e) {
log.error("Exception while stopping OSGi framework", e);
} finally {
for(Closeable c : toClose) {
try {
c.close();
} catch(IOException ignore) {
}
}
}
}
}
});
}
}