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 }