001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.servicemix.specs.locator;
018    
019    import java.io.BufferedReader;
020    import java.io.InputStreamReader;
021    import java.net.URL;
022    import java.util.Enumeration;
023    import java.util.HashMap;
024    import java.util.Map;
025    import java.util.concurrent.Callable;
026    import java.util.concurrent.ConcurrentHashMap;
027    import java.util.concurrent.ConcurrentMap;
028    
029    import org.osgi.framework.Bundle;
030    import org.osgi.framework.BundleActivator;
031    import org.osgi.framework.BundleContext;
032    import org.osgi.framework.BundleEvent;
033    import org.osgi.framework.SynchronousBundleListener;
034    
035    public class Activator implements BundleActivator, SynchronousBundleListener {
036    
037        private static boolean debug = false;
038    
039        private ConcurrentMap<Long, Map<String, Callable<Class>>> factories = new ConcurrentHashMap<Long, Map<String, Callable<Class>>>();
040    
041        private BundleContext bundleContext;
042    
043        static {
044            try {
045                String prop = System.getProperty("org.apache.servicemix.specs.debug");
046                debug = prop != null && !"false".equals(prop);
047            } catch (Throwable t) { }
048        }
049    
050        /**
051         * <p>Output debugging messages.</p>
052         *
053         * @param msg <code>String</code> to print to <code>stderr</code>.
054         */
055        protected void debugPrintln(String msg) {
056            if (debug) {
057                System.err.println("Spec(" + bundleContext.getBundle().getBundleId() + "): " + msg);
058            }
059        }
060    
061        public synchronized void start(BundleContext bundleContext) throws Exception {
062            this.bundleContext = bundleContext;
063            debugPrintln("activating");
064            debugPrintln("adding bundle listener");
065            bundleContext.addBundleListener(this);
066            debugPrintln("checking existing bundles");
067            for (Bundle bundle : bundleContext.getBundles()) {
068                if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.STARTING ||
069                        bundle.getState() == Bundle.ACTIVE || bundle.getState() == Bundle.STOPPING) {
070                    register(bundle);
071                }
072            }
073            debugPrintln("activated");
074        }
075    
076        public synchronized void stop(BundleContext bundleContext) throws Exception {
077            debugPrintln("deactivating");
078            bundleContext.removeBundleListener(this);
079            while (!factories.isEmpty()) {
080                unregister(factories.keySet().iterator().next());
081            }
082            debugPrintln("deactivated");
083            this.bundleContext = null;
084        }
085    
086        public void bundleChanged(BundleEvent event) {
087            if (event.getType() == BundleEvent.RESOLVED) {
088                register(event.getBundle());
089            } else if (event.getType() == BundleEvent.UNRESOLVED || event.getType() == BundleEvent.UNINSTALLED) {
090                unregister(event.getBundle().getBundleId());
091            }
092        }
093    
094        protected void register(final Bundle bundle) {
095            debugPrintln("checking bundle " + bundle.getBundleId());
096            Map<String, Callable<Class>> map = factories.get(bundle.getBundleId());
097            Enumeration e = bundle.findEntries("META-INF/services/", "*", false);
098            if (e != null) {
099                while (e.hasMoreElements()) {
100                    final URL u = (URL) e.nextElement();
101                    final String url = u.toString();
102                    if (url.endsWith("/")) {
103                        continue;
104                    }
105                    final String factoryId = url.substring(url.lastIndexOf("/") + 1);
106                    if (map == null) {
107                        map = new HashMap<String, Callable<Class>>();
108                        factories.put(bundle.getBundleId(), map);
109                    }
110                    map.put(factoryId, new BundleFactoryLoader(factoryId, u, bundle));
111                }
112            }
113            if (map != null) {
114                for (Map.Entry<String, Callable<Class>> entry : map.entrySet()) {
115                    debugPrintln("registering service for key " + entry.getKey() + "with value " + entry.getValue());
116                    OsgiLocator.register(entry.getKey(), entry.getValue());
117                }
118            }
119        }
120    
121        protected void unregister(long bundleId) {
122            Map<String, Callable<Class>> map = factories.remove(bundleId);
123            if (map != null) {
124                for (Map.Entry<String, Callable<Class>> entry : map.entrySet()) {
125                    debugPrintln("unregistering service for key " + entry.getKey() + "with value " + entry.getValue());
126                    OsgiLocator.unregister(entry.getKey(), entry.getValue());
127                }
128            }
129        }
130    
131        private class BundleFactoryLoader implements Callable<Class> {
132            private final String factoryId;
133            private final URL u;
134            private final Bundle bundle;
135    
136            public BundleFactoryLoader(String factoryId, URL u, Bundle bundle) {
137                this.factoryId = factoryId;
138                this.u = u;
139                this.bundle = bundle;
140            }
141    
142            public Class call() throws Exception {
143                try {
144                    debugPrintln("creating factory for key: " + factoryId);
145                    BufferedReader br = new BufferedReader(new InputStreamReader(u.openStream(), "UTF-8"));
146                    String factoryClassName = br.readLine();
147                    br.close();
148                    debugPrintln("factory implementation: " + factoryClassName);
149                    return bundle.loadClass(factoryClassName);
150                } catch (Exception e) {
151                    debugPrintln("exception caught while creating factory: " + e);
152                    throw e;
153                } catch (Error e) {
154                    debugPrintln("error caught while creating factory: " + e);
155                    throw e;
156                }
157            }
158    
159            @Override
160            public String toString() {
161               return u.toString();
162            }
163    
164            @Override
165            public int hashCode() {
166               return u.hashCode();
167            }
168    
169            @Override
170            public boolean equals(Object obj) {
171                if (obj instanceof BundleFactoryLoader) {
172                    return u.equals(((BundleFactoryLoader) obj).u);
173                } else {
174                    return false;
175                }
176            }
177        }
178    }