/* * Copyright (c) 2003-2006, KNOPFLERFISH project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * - Neither the name of the KNOPFLERFISH project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @author Erik Wistrand * @author Philippe Laporte */ package org.knopflerfish.util.metatype; import org.osgi.framework.*; import org.osgi.service.metatype.*; import org.osgi.service.cm.*; import org.osgi.util.tracker.*; import org.knopflerfish.service.log.LogRef; import java.util.*; import java.util.zip.ZipEntry; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.*; /** * Class which monitors installed bundles for metatype and CM default data. * *

* When instanciated, SystemMetatypeProvider will listen for installed * bundles and try to extract metatype and Cm defaults XML from the * bundle jar files. This data will then be available using the * getServicePIDs, getFactoryPIDs and * getObjectClassDefinition methods. *

*/ public class SystemMetatypeProvider extends MTP implements MetaTypeService { /** * Default URL to metatype XML. * *

* Value is "!/metatype.xml" *

*/ public static final String METATYPE_RESOURCE = "/metatype.xml"; /** * Default URL to default CM values * *

* Value is "!/cmdefaults.xml" *

*/ public static final String CMDEFAULTS_RESOURCE = "/cmdefaults.xml"; /** * Manifest attribute name specifying metatype XML URL. * *

* Value is "Bundle-MetatypeURL" *

*/ public static final String ATTRIB_METATYPEURL = "Bundle-MetatypeURL"; /** * Manifest attribute name specifying CM defaults XML URL. * *

* Value is "Bundle-CMDefaultsURL" *

*/ public static final String ATTRIB_CMDEFAULTSURL = "Bundle-CMDefaultsURL"; BundleContext bc; LogRef log; // Bundle -> MTP Map providers = new Hashtable(); ServiceTracker cmTracker; // Special MTP which tracks CM configuration instances MTP cmMTP; // String (pid) -> OCD Map cmOCDMap = new HashMap(); /** * Create a SystemMetatypeProvider, using the specified bundle context * for listeners. */ public SystemMetatypeProvider(BundleContext bc) { super("system"); this.bc = bc; log = new LogRef(bc); } BundleListener bl = null; /** * Start listening for bundles. */ public void open() { if(bl != null) { return; } bl = new BundleListener() { public void bundleChanged(BundleEvent ev) { switch(ev.getType()) { case BundleEvent.INSTALLED: //We can't read properties from the system bundle if(ev.getBundle().getBundleId() != 0) { try { loadMTP(ev.getBundle()); } catch (Exception e) { log.error("Failed to handle bundle " + ev.getBundle().getBundleId(), e); //e.printStackTrace(System.out); } } break; case BundleEvent.UNINSTALLED: Bundle b = ev.getBundle(); if(b.getBundleId() != 0) { providers.remove(b); } break; } } }; Bundle[] bs = bc.getBundles(); for(int i = 0; i < bs.length; i++) { bl.bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bs[i])); } bc.addBundleListener(bl); cmTracker = new ServiceTracker(bc, ConfigurationAdmin.class.getName(), null); cmTracker.open(); // track CM configuration instances as MTPs on the system bundle. cmMTP = new MTP("[CM]") { public String[] getPids() { return MTP.toStringArray(getCMServicePIDs()); } public String[] getFactoryPids() { return MTP.toStringArray(getCMFactoryPIDs()); } public String[] getLocales() { return null; } public ObjectClassDefinition getObjectClassDefinition(String pid, String locale) { OCD ocd = (OCD)cmOCDMap.get(pid); if(ocd != null) { //cached return ocd; } ConfigurationAdmin ca = (ConfigurationAdmin)cmTracker.getService(); if(ca != null) { try { Configuration[] configs = ca.listConfigurations(null); Configuration conf = null; for(int i = 0; configs != null && i < configs.length; i++) { if(pid.equals(configs[i].getPid()) || pid.equals(configs[i].getFactoryPid())) { conf = configs[i]; } } if(conf != null) { Dictionary props = conf.getProperties(); ocd = new OCD(pid, pid, pid + " from CM", props); cmOCDMap.put(pid, ocd); return ocd; } else { throw new RuntimeException("No config for pid " + pid); } } catch (Exception e) { log.error("Failed to get service pid " + pid, e); return null; } } else { log.warn("Failed to get CA when getting pid " + pid); return null; } } }; setupMTListener(); } /** * Stop listening for bundles. */ public void close() { if(cmTracker != null) { cmTracker.close(); cmTracker = null; } if(bl != null) { bc.removeBundleListener(bl); } bl = null; } /** * Explictly load a metatype provider from a bundle and cache * it for later retrival by getMTP. * * @throws Exception if loading fails */ public void loadMTP(Bundle b) throws Exception { URL url; //try R4 first Enumeration metaTypeFiles = b.findEntries(MetaTypeService.METATYPE_DOCUMENTS_LOCATION, "*", false); if(metaTypeFiles != null){ BundleMetaTypeResource bmtr = new BundleMetaTypeResource(b); while(metaTypeFiles.hasMoreElements()){ url = (URL)metaTypeFiles.nextElement(); bmtr.mergeWith(Loader.loadBMTIfromUrl(bc, b, url)); } bmtr.prepare(); providers.put(b, bmtr); } else{ //proprietary legacy MTP mtp = null; String defStr = (String)b.getHeaders().get(ATTRIB_METATYPEURL); if(defStr == null || "".equals(defStr)) { defStr = METATYPE_RESOURCE; } if(defStr.startsWith("!")) { url = b.getEntry(defStr.substring(1)); } else if(defStr.startsWith("/")) { url = b.getEntry(defStr); } else { url = new URL(defStr); } if(url != null) { try { mtp = Loader.loadMTPFromURL(b, url); providers.put(b, mtp); } catch (Exception e) { log.info("Failed to load metatype XML from bundle " + b.getBundleId(), e); //throw e; } } //defaults are specified in the file itself in R4 String valStr = (String)b.getHeaders().get(ATTRIB_CMDEFAULTSURL); if(valStr == null || "".equals(valStr)) { valStr = CMDEFAULTS_RESOURCE; } if(valStr.startsWith("!")) { url = b.getEntry(valStr.substring(1)); } else if(valStr.startsWith("/")) { url = b.getEntry(valStr); } else { url = new URL(valStr); } if(url != null) { try { Loader.loadDefaultsFromURL(mtp, url); log.info("Bundle " + b.getBundleId() + ": loaded default values"); } catch (Exception e) { log.info("Failed to load cm defaults XML from bundle " + b.getBundleId(), e); //throw e; } } } //proprietary legacy } public String[] getPids() { synchronized(providers) { Set set = new HashSet(); for(Iterator it = providers.keySet().iterator(); it.hasNext();) { Bundle b = (Bundle)it.next(); MetaTypeInformation mtp = (MetaTypeInformation)providers.get(b); String[] pids = mtp.getPids(); for(int i = 0; pids != null && i < pids.length; i++) { set.add(pids[i]); } } for(Iterator it = mtMap.keySet().iterator(); it.hasNext();) { ServiceReference sr = (ServiceReference)it.next(); try { String[] pids = (String[])sr.getProperty("service.pids"); for(int i = 0; pids != null && i < pids.length; i++) { set.add(pids[i]); } } catch (Exception e) { log.warn("No service.pids property on " + sr); } } return MTP.toStringArray(set); } } public String[] getFactoryPids() { synchronized(providers) { Set set = new HashSet(); for(Iterator it = providers.keySet().iterator(); it.hasNext();) { Bundle b = (Bundle)it.next(); MetaTypeInformation mtp = (MetaTypeInformation)providers.get(b); String[] pids = mtp.getFactoryPids(); for(int i = 0; pids != null && i < pids.length; i++) { set.add(pids[i]); } } for(Iterator it = mtMap.keySet().iterator(); it.hasNext();) { ServiceReference sr = (ServiceReference)it.next(); try { String[] pids = (String[])sr.getProperty("factory.pids"); for(int i = 0; pids != null && i < pids.length; i++) { set.add(pids[i]); } } catch (Exception e) { log.warn("No factory.pids property on " + sr); } } return MTP.toStringArray(set); } } public String[] getLocales() { return null; } /** * Get a loaded metatype provider, given a bundle. * * @return Provider if such provider is found, otherwise null. */ public MetaTypeInformation getMTP(Bundle b) { ServiceReference cmSR = cmTracker.getServiceReference(); MetaTypeInformation mti = null; if(cmSR != null && cmSR.getBundle() == b) { mti = cmMTP; } else if(b.getBundleId() == 0) { mti = this; } else { mti = (MetaTypeInformation)providers.get(b); } return mti; } /** * Get an ObjectClassDefinition given a PID. * * @return ObjectClassDefinition if PID exists, otherwise null. */ public ObjectClassDefinition getObjectClassDefinition(String pid, String locale) { synchronized(providers) { for(Iterator it = providers.keySet().iterator(); it.hasNext();) { Bundle b = (Bundle)it.next(); MetaTypeProvider mtp = (MetaTypeProvider)providers.get(b); ObjectClassDefinition ocd = mtp.getObjectClassDefinition(pid, locale); if(ocd != null) { return ocd; } } synchronized(mtMap) { for(Iterator it = mtMap.keySet().iterator(); it.hasNext();) { ServiceReference sr = (ServiceReference)it.next(); MetaTypeProvider mt = (MetaTypeProvider)mtMap.get(sr); ObjectClassDefinition ocd = mt.getObjectClassDefinition(pid, locale); if(ocd != null) { return ocd; } } } return null; } } ServiceListener mtListener = null; // ServiceReference -> MetatypeProvider Map mtMap = new HashMap(); void setupMTListener() { mtListener = new ServiceListener() { public void serviceChanged(ServiceEvent ev) { ServiceReference sr = ev.getServiceReference(); synchronized(mtMap) { switch(ev.getType()) { case ServiceEvent.REGISTERED: MetaTypeProvider mt = (MetaTypeProvider)bc.getService(sr); if(mt != SystemMetatypeProvider.this) { mtMap.put(sr, mt); } break; case ServiceEvent.UNREGISTERING: bc.ungetService(sr); mtMap.remove(sr); break; } } } }; try { String filter = "(objectclass=" + MetaTypeProvider.class.getName() + ")"; ServiceReference[] srl = bc.getServiceReferences(null, filter); for(int i = 0; srl != null && i < srl.length; i++) { mtListener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, srl[i])); } bc.addServiceListener(mtListener, filter); } catch(Exception e) { log.error("Failed to get other providers", e); } } Set getCMServicePIDs() { Set pids = new HashSet(); ConfigurationAdmin ca = (ConfigurationAdmin)cmTracker.getService(); if(ca != null) { try { Configuration[] configs = ca.listConfigurations("(service.pid=*)"); for(int i = 0; configs != null && i < configs.length; i++) { if(configs[i].getFactoryPid() == null) { pids.add(configs[i].getPid()); } } } catch (Exception e) { log.error("Failed to get service pids", e); } } return pids; } Set getCMFactoryPIDs() { Set pids = new HashSet(); ConfigurationAdmin ca = (ConfigurationAdmin)cmTracker.getService(); if(ca != null) { try { Configuration[] configs = ca.listConfigurations("(service.pid=*)"); for(int i = 0; configs != null && i < configs.length; i++) { if(configs[i].getFactoryPid() != null) { pids.add(configs[i].getFactoryPid()); } } } catch (Exception e) { log.error("Failed to get service pids", e); } } return pids; } public MetaTypeInformation getMetaTypeInformation(Bundle bundle) { MetaTypeInformation mti; mti = (MetaTypeInformation)providers.get(bundle); if(mti != null){ return mti; } synchronized(mtMap) { for(Iterator it = mtMap.keySet().iterator(); it.hasNext();) { ServiceReference sr = (ServiceReference)it.next(); if(sr.getBundle() == bundle){ MetaTypeProvider mtp = (MetaTypeProvider)mtMap.get(sr); if(!(mtp instanceof MetaTypeInformation)){ return new BundleMetaTypeProvider(mtp, sr); } else{ return (MetaTypeInformation) mtp; } } } return null; } } } class BundleMetaTypeProvider implements MetaTypeInformation{ private MetaTypeProvider mtp; private Bundle bundle; //id -> MetaData private String[] pids; private String[] factoryPids; public BundleMetaTypeProvider(MetaTypeProvider mtp, ServiceReference sr){ this.mtp = mtp; this.bundle = sr.getBundle(); if(mtp instanceof ManagedService){ pids = new String[1]; pids[0] = (String) sr.getProperty(Constants.SERVICE_PID); factoryPids = new String[0]; } else if(mtp instanceof ManagedServiceFactory){ factoryPids = new String[1]; factoryPids[0] = (String) sr.getProperty(Constants.SERVICE_PID); pids = new String[0]; } } public Bundle getBundle() { return bundle; } public String[] getFactoryPids() { return factoryPids; } public String[] getPids() { return pids; } public String[] getLocales() { return mtp.getLocales(); } public ObjectClassDefinition getObjectClassDefinition(String id, String locale) { return mtp.getObjectClassDefinition(id, locale); } } //class