/*
* (C) Copyright 2006-2011 Nuxeo SA (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:
* Nuxeo - initial API and implementation
* $Id$
*/
package org.nuxeo.osgi;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.common.Environment;
import org.nuxeo.common.collections.ListenerList;
import org.nuxeo.osgi.services.PackageAdminImpl;
import org.nuxeo.osgi.util.jar.JarFileCloser;
import org.nuxeo.osgi.util.jar.URLJarFileIntrospector;
import org.nuxeo.osgi.util.jar.URLJarFileIntrospectionError;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.packageadmin.PackageAdmin;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class OSGiAdapter {
private static final Log log = LogFactory.getLog(OSGiAdapter.class);
protected final File workingDir;
protected final File dataDir;
protected File idTableFile;
protected BundleIdGenerator bundleIds;
protected ListenerList frameworkListeners;
protected ListenerList bundleListeners;
protected ListenerList serviceListeners;
protected Map<String, ServiceRegistration> services;
protected BundleRegistry registry;
protected Properties properties;
protected SystemBundle systemBundle;
protected JarFileCloser uRLJarFileCloser;
public OSGiAdapter(File workingDir) {
this(workingDir,
new File(System.getProperty(Environment.NUXEO_DATA_DIR, workingDir + File.separator + "data")),
new Properties());
}
public OSGiAdapter(File workingDir, File dataDir, Properties properties) {
services = new ConcurrentHashMap<String, ServiceRegistration>();
this.workingDir = workingDir;
this.dataDir = dataDir;
this.dataDir.mkdirs();
this.workingDir.mkdirs();
initialize(properties);
}
public void removeService(String clazz) {
services.remove(clazz);
}
protected void initialize(Properties properties) {
this.properties = properties == null ? new Properties() : properties;
registry = new BundleRegistry();
frameworkListeners = new ListenerList();
bundleListeners = new ListenerList();
serviceListeners = new ListenerList();
bundleIds = new BundleIdGenerator();
idTableFile = new File(dataDir, "bundles.ids");
bundleIds.load(idTableFile);
// setting up default properties
properties.put(Constants.FRAMEWORK_VENDOR, "Nuxeo");
properties.put(Constants.FRAMEWORK_VERSION, "1.0.0");
}
public void setSystemBundle(SystemBundle systemBundle) throws BundleException {
if (this.systemBundle != null) {
throw new IllegalStateException("Cannot set system bundle");
}
install(systemBundle);
registry.addBundleAlias("system.bundle", systemBundle.getSymbolicName());
this.systemBundle = systemBundle;
systemBundle.getBundleContext().registerService(PackageAdmin.class.getName(), new PackageAdminImpl(this), null);
}
public BundleRegistry getRegistry() {
return registry;
}
public String getProperty(String key) {
String value = properties.getProperty(key);
if (value == null) {
value = System.getProperty(key);
}
return value;
}
public String getProperty(String key, String defvalue) {
String val = getProperty(key);
if (val == null) {
val = defvalue;
}
return val;
}
/**
* @param name the property name.
* @param value the property value.
*/
public void setProperty(String name, String value) {
properties.put(name, value);
}
public void shutdown() throws IOException {
bundleIds.store(idTableFile);
registry.shutdown();
properties.clear();
registry = null;
frameworkListeners = null;
bundleListeners = null;
serviceListeners = null;
properties = null;
uRLJarFileCloser = null;
}
public long getBundleId(String symbolicName) {
return bundleIds.getBundleId(symbolicName);
}
public File getWorkingDir() {
return workingDir;
}
public File getDataDir() {
return dataDir;
}
public BundleImpl getBundle(String symbolicName) {
return registry.getBundle(symbolicName);
}
public BundleImpl[] getInstalledBundles() {
return registry.getInstalledBundles();
}
public void install(BundleImpl bundle) throws BundleException {
double s = System.currentTimeMillis();
registry.install(bundle);
bundle.startupTime = System.currentTimeMillis() - s;
}
public void uninstall(BundleImpl bundle) throws BundleException {
registry.uninstall(bundle);
}
public void addFrameworkListener(FrameworkListener listener) {
frameworkListeners.add(listener);
}
public void removeFrameworkListener(FrameworkListener listener) {
frameworkListeners.remove(listener);
}
public void addServiceListener(ServiceListener listener) {
serviceListeners.add(listener);
}
public void addServiceListener(ServiceListener listener, String filter) {
// TODO?
throw new UnsupportedOperationException("This method is not implemented");
}
public void removeServiceListener(ServiceListener listener) {
serviceListeners.remove(listener);
}
public void addBundleListener(BundleListener listener) {
bundleListeners.add(listener);
}
public void removeBundleListener(BundleListener listener) {
bundleListeners.remove(listener);
}
public void fireFrameworkEvent(FrameworkEvent event) {
log.debug("Firing FrameworkEvent on " + frameworkListeners.size() + " listeners");
if (event.getType() == FrameworkEvent.STARTED) {
uRLJarFileCloser = newJarFileCloser();
}
Object[] listeners = frameworkListeners.getListeners();
for (Object listener : listeners) {
log.debug("Start execution of " + listener.getClass() + " listener");
try {
((FrameworkListener) listener).frameworkEvent(event);
log.debug("End execution of " + listener.getClass() + " listener");
} catch (RuntimeException e) {
log.error("Error during Framework Listener execution : " + listener.getClass(), e);
}
}
}
protected JarFileCloser newJarFileCloser() {
try {
URLJarFileIntrospector introspector = new URLJarFileIntrospector();
return introspector.newJarFileCloser(systemBundle.loader);
} catch (URLJarFileIntrospectionError cause) {
log.warn("Cannot put URL jar file closer in place", cause);
return JarFileCloser.NOOP;
}
}
public void fireServiceEvent(ServiceEvent event) {
Object[] listeners = serviceListeners.getListeners();
for (Object listener : listeners) {
((ServiceListener) listener).serviceChanged(event);
}
}
public void fireBundleEvent(BundleEvent event) {
Object[] listeners = bundleListeners.getListeners();
for (Object listener : listeners) {
((BundleListener) listener).bundleChanged(event);
}
}
public Bundle getSystemBundle() {
return systemBundle;
}
/**
* helper for closing jar files during bundle uninstall
*
* @since 5.6
*/
public JarFileCloser getURLJarFileCloser() {
if (uRLJarFileCloser == null) {
uRLJarFileCloser = newJarFileCloser();
}
return uRLJarFileCloser;
}
}