001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors.
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 *     bstefanescu, atchertchian, jcarsique
019 */
020
021package org.nuxeo.osgi;
022
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.LinkedHashMap;
027import java.util.Map;
028import java.util.Set;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.osgi.framework.Bundle;
033import org.osgi.framework.BundleException;
034import org.osgi.framework.Constants;
035
036/**
037 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
038 */
039public class BundleRegistry {
040
041    private static final Log log = LogFactory.getLog(BundleRegistry.class);
042
043    private final Map<Long, BundleRegistration> bundlesById;
044
045    private final Map<String, BundleRegistration> bundles;
046
047    private final Map<String, Set<BundleRegistration>> pendings;
048
049    public BundleRegistry() {
050        bundlesById = new HashMap<Long, BundleRegistration>();
051        bundles = new LinkedHashMap<String, BundleRegistration>();
052        pendings = new HashMap<String, Set<BundleRegistration>>();
053    }
054
055    public void addBundleAlias(String alias, String symbolicName) {
056        BundleRegistration breg = bundles.get(symbolicName);
057        if (breg != null) {
058            bundles.put(alias, breg);
059        }
060    }
061
062    public synchronized BundleImpl getBundle(long id) {
063        BundleRegistration reg = bundlesById.get(id);
064        return reg == null ? null : reg.bundle;
065    }
066
067    public synchronized BundleImpl getBundle(String symbolicName) {
068        BundleRegistration reg = bundles.get(symbolicName);
069        return reg == null ? null : reg.bundle;
070    }
071
072    /**
073     * @since 5.6
074     */
075    public synchronized BundleImpl[] getFragments(String symbolicName) {
076        BundleRegistration reg = bundles.get(symbolicName);
077
078        ArrayList<BundleImpl> fragments = new ArrayList<BundleImpl>();
079        for (String id : reg.extendsMe) {
080            fragments.add(getBundle(id));
081        }
082        return fragments.toArray(new BundleImpl[fragments.size()]);
083    }
084
085    public synchronized BundleImpl[] getInstalledBundles() {
086        BundleImpl[] bundles = new BundleImpl[this.bundles.size()];
087        int i = 0;
088        for (BundleRegistration reg : this.bundles.values()) {
089            bundles[i++] = reg.bundle;
090        }
091        return bundles;
092    }
093
094    public synchronized void install(BundleImpl bundle) throws BundleException {
095        if (bundle.getState() == Bundle.UNINSTALLED) {
096            BundleRegistration reg = bundles.get(bundle.getSymbolicName());
097            if (reg == null) {
098                register(new BundleRegistration(bundle));
099            } else {
100                register(reg);
101            }
102        }
103    }
104
105    public synchronized void uninstall(BundleImpl bundle) throws BundleException {
106        if (bundle.getState() != Bundle.UNINSTALLED) {
107            BundleRegistration reg = bundles.get(bundle.getSymbolicName());
108            if (reg != null) {
109                unregister(reg);
110            }
111        }
112    }
113
114    private void register(BundleRegistration reg) throws BundleException {
115        String hostBundleId = getFragmentHost(reg);
116        if (hostBundleId != null) {
117            BundleRegistration host = bundles.get(hostBundleId);
118            if (host == null) {
119                reg.addUnresolvedDependency(hostBundleId);
120            }
121        }
122        if (reg.hasUnresolvedDependencies()) {
123            doPostpone(reg);
124        } else {
125            doRegister(reg);
126        }
127    }
128
129    protected void unregister(BundleRegistration reg) throws BundleException {
130        if (getFragmentHost(reg) == null) {
131            reg.bundle.stop();
132        }
133
134        reg.bundle.setUnResolved();
135        bundles.remove(reg.bundle.getSymbolicName());
136        bundlesById.remove(reg.bundle.getBundleId());
137        reg.bundle.setUninstalled();
138        for (String depOnMe : reg.dependsOnMe) {
139            BundleRegistration depReg = bundles.get(depOnMe);
140            if (depReg != null) { // set to unresolved
141                depReg.bundle.setUnResolved();
142            }
143        }
144    }
145
146    protected void doPostpone(BundleRegistration reg) {
147        String name = reg.bundle.getSymbolicName();
148        log.info("Registering unresolved bundle: " + name);
149        bundles.put(name, reg);
150        bundlesById.put(reg.bundle.getBundleId(), reg);
151
152        for (String dep : reg.waitingFor) {
153            Set<BundleRegistration> regs = pendings.get(dep);
154            if (regs == null) {
155                regs = new HashSet<BundleRegistration>();
156                pendings.put(dep, regs);
157            }
158            regs.add(reg);
159        }
160        reg.bundle.setInstalled();
161    }
162
163    protected void doRegister(BundleRegistration reg) throws BundleException {
164        String name = reg.bundle.getSymbolicName();
165        log.info("Registering resolved bundle: " + name);
166        bundles.put(name, reg);
167        bundlesById.put(reg.bundle.getBundleId(), reg);
168        reg.bundle.setInstalled();
169        reg.bundle.setResolved();
170
171        String hostBundleId = getFragmentHost(reg);
172        if (hostBundleId != null) {
173            BundleRegistration host = bundles.get(hostBundleId);
174            host.addFragment(reg.bundle.getSymbolicName());
175        } else {
176            // TODO how to lazy start the bundle?
177            reg.bundle.start();
178        }
179
180        // check if there are objects waiting for me
181        Set<BundleRegistration> regs = pendings.remove(name);
182        if (regs != null) {
183            for (BundleRegistration pendingReg : regs) {
184                pendingReg.removeUnresolvedDependency(name);
185                if (!pendingReg.hasUnresolvedDependencies()) {
186                    doRegister(pendingReg);
187                }
188            }
189        }
190    }
191
192    private String getFragmentHost(BundleRegistration reg) {
193        String hostBundleId = reg.bundle.getHeaders().get(Constants.FRAGMENT_HOST);
194        if (hostBundleId == null) {
195            return null;
196        }
197        int p = hostBundleId.indexOf(';');
198        if (p > -1) { // remove version or other extra information if any
199            hostBundleId = hostBundleId.substring(0, p);
200        }
201        return hostBundleId;
202    }
203
204    public void shutdown() {
205        BundleRegistration[] regs = bundles.values().toArray(new BundleRegistration[bundles.size()]);
206        for (BundleRegistration reg : regs) {
207            try {
208                if (reg.bundle != null) {
209                    reg.bundle.shutdown();
210                }
211            } catch (BundleException e) {
212                log.error("Failed to stop bundle " + reg.bundle.getSymbolicName(), e);
213            } catch (RuntimeException e) {
214                log.error("Failed to stop bundle " + reg.bundle.getSymbolicName(), e);
215            }
216        }
217    }
218
219}