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 }