001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (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 *     Nuxeo - initial API and implementation
018 * $Id$
019 */
020
021package org.nuxeo.osgi;
022
023import java.io.File;
024import java.io.IOException;
025import java.util.Map;
026import java.util.Properties;
027import java.util.concurrent.ConcurrentHashMap;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.common.Environment;
032import org.nuxeo.common.collections.ListenerList;
033import org.nuxeo.osgi.services.PackageAdminImpl;
034import org.nuxeo.osgi.util.jar.JarFileCloser;
035import org.nuxeo.osgi.util.jar.URLJarFileIntrospector;
036import org.nuxeo.osgi.util.jar.URLJarFileIntrospectionError;
037import org.osgi.framework.Bundle;
038import org.osgi.framework.BundleEvent;
039import org.osgi.framework.BundleException;
040import org.osgi.framework.BundleListener;
041import org.osgi.framework.Constants;
042import org.osgi.framework.FrameworkEvent;
043import org.osgi.framework.FrameworkListener;
044import org.osgi.framework.ServiceEvent;
045import org.osgi.framework.ServiceListener;
046import org.osgi.framework.ServiceRegistration;
047import org.osgi.service.packageadmin.PackageAdmin;
048
049/**
050 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
051 */
052public class OSGiAdapter {
053
054    private static final Log log = LogFactory.getLog(OSGiAdapter.class);
055
056    protected final File workingDir;
057
058    protected final File dataDir;
059
060    protected File idTableFile;
061
062    protected BundleIdGenerator bundleIds;
063
064    protected ListenerList frameworkListeners;
065
066    protected ListenerList bundleListeners;
067
068    protected ListenerList serviceListeners;
069
070    protected Map<String, ServiceRegistration> services;
071
072    protected BundleRegistry registry;
073
074    protected Properties properties;
075
076    protected SystemBundle systemBundle;
077
078    protected JarFileCloser uRLJarFileCloser;
079
080    public OSGiAdapter(File workingDir) {
081        this(workingDir,
082                new File(System.getProperty(Environment.NUXEO_DATA_DIR, workingDir + File.separator + "data")),
083                new Properties());
084    }
085
086    public OSGiAdapter(File workingDir, File dataDir, Properties properties) {
087        services = new ConcurrentHashMap<String, ServiceRegistration>();
088        this.workingDir = workingDir;
089        this.dataDir = dataDir;
090        this.dataDir.mkdirs();
091        this.workingDir.mkdirs();
092        initialize(properties);
093    }
094
095    public void removeService(String clazz) {
096        services.remove(clazz);
097    }
098
099    protected void initialize(Properties properties) {
100        this.properties = properties == null ? new Properties() : properties;
101        registry = new BundleRegistry();
102        frameworkListeners = new ListenerList();
103        bundleListeners = new ListenerList();
104        serviceListeners = new ListenerList();
105        bundleIds = new BundleIdGenerator();
106        idTableFile = new File(dataDir, "bundles.ids");
107        bundleIds.load(idTableFile);
108        // setting up default properties
109        properties.put(Constants.FRAMEWORK_VENDOR, "Nuxeo");
110        properties.put(Constants.FRAMEWORK_VERSION, "1.0.0");
111    }
112
113    public void setSystemBundle(SystemBundle systemBundle) throws BundleException {
114        if (this.systemBundle != null) {
115            throw new IllegalStateException("Cannot set system bundle");
116        }
117        install(systemBundle);
118        registry.addBundleAlias("system.bundle", systemBundle.getSymbolicName());
119        this.systemBundle = systemBundle;
120
121        systemBundle.getBundleContext().registerService(PackageAdmin.class.getName(), new PackageAdminImpl(this), null);
122    }
123
124    public BundleRegistry getRegistry() {
125        return registry;
126    }
127
128    public String getProperty(String key) {
129        String value = properties.getProperty(key);
130        if (value == null) {
131            value = System.getProperty(key);
132        }
133        return value;
134    }
135
136    public String getProperty(String key, String defvalue) {
137        String val = getProperty(key);
138        if (val == null) {
139            val = defvalue;
140        }
141        return val;
142    }
143
144    /**
145     * @param name the property name.
146     * @param value the property value.
147     */
148    public void setProperty(String name, String value) {
149        properties.put(name, value);
150    }
151
152    public void shutdown() throws IOException {
153        bundleIds.store(idTableFile);
154        registry.shutdown();
155        properties.clear();
156        registry = null;
157        frameworkListeners = null;
158        bundleListeners = null;
159        serviceListeners = null;
160        properties = null;
161        uRLJarFileCloser = null;
162    }
163
164    public long getBundleId(String symbolicName) {
165        return bundleIds.getBundleId(symbolicName);
166    }
167
168    public File getWorkingDir() {
169        return workingDir;
170    }
171
172    public File getDataDir() {
173        return dataDir;
174    }
175
176    public BundleImpl getBundle(String symbolicName) {
177        return registry.getBundle(symbolicName);
178    }
179
180    public BundleImpl[] getInstalledBundles() {
181        return registry.getInstalledBundles();
182    }
183
184    public void install(BundleImpl bundle) throws BundleException {
185        double s = System.currentTimeMillis();
186        registry.install(bundle);
187        bundle.startupTime = System.currentTimeMillis() - s;
188    }
189
190    public void uninstall(BundleImpl bundle) throws BundleException {
191        registry.uninstall(bundle);
192    }
193
194    public void addFrameworkListener(FrameworkListener listener) {
195        frameworkListeners.add(listener);
196    }
197
198    public void removeFrameworkListener(FrameworkListener listener) {
199        frameworkListeners.remove(listener);
200    }
201
202    public void addServiceListener(ServiceListener listener) {
203        serviceListeners.add(listener);
204    }
205
206    public void addServiceListener(ServiceListener listener, String filter) {
207        // TODO?
208        throw new UnsupportedOperationException("This method is not implemented");
209    }
210
211    public void removeServiceListener(ServiceListener listener) {
212        serviceListeners.remove(listener);
213    }
214
215    public void addBundleListener(BundleListener listener) {
216        bundleListeners.add(listener);
217    }
218
219    public void removeBundleListener(BundleListener listener) {
220        bundleListeners.remove(listener);
221    }
222
223    public void fireFrameworkEvent(FrameworkEvent event) {
224        log.debug("Firing FrameworkEvent on " + frameworkListeners.size() + " listeners");
225        if (event.getType() == FrameworkEvent.STARTED) {
226            uRLJarFileCloser = newJarFileCloser();
227        }
228        Object[] listeners = frameworkListeners.getListeners();
229        for (Object listener : listeners) {
230            log.debug("Start execution of " + listener.getClass() + " listener");
231            try {
232                ((FrameworkListener) listener).frameworkEvent(event);
233                log.debug("End execution of " + listener.getClass() + " listener");
234            } catch (RuntimeException e) {
235                log.error("Error during Framework Listener execution : " + listener.getClass(), e);
236            }
237        }
238    }
239
240    protected JarFileCloser newJarFileCloser() {
241        try {
242            URLJarFileIntrospector introspector = new URLJarFileIntrospector();
243            return introspector.newJarFileCloser(systemBundle.loader);
244        } catch (URLJarFileIntrospectionError cause) {
245            log.warn("Cannot put URL jar file closer in place", cause);
246            return JarFileCloser.NOOP;
247        }
248    }
249
250    public void fireServiceEvent(ServiceEvent event) {
251        Object[] listeners = serviceListeners.getListeners();
252        for (Object listener : listeners) {
253            ((ServiceListener) listener).serviceChanged(event);
254        }
255    }
256
257    public void fireBundleEvent(BundleEvent event) {
258        Object[] listeners = bundleListeners.getListeners();
259        for (Object listener : listeners) {
260            ((BundleListener) listener).bundleChanged(event);
261        }
262    }
263
264    public Bundle getSystemBundle() {
265        return systemBundle;
266    }
267
268    /**
269     * helper for closing jar files during bundle uninstall
270     *
271     * @since 5.6
272     */
273    public JarFileCloser getURLJarFileCloser() {
274        if (uRLJarFileCloser == null) {
275            uRLJarFileCloser = newJarFileCloser();
276        }
277        return uRLJarFileCloser;
278    }
279}