| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.aries.blueprint.compendium.cm; |
| |
| import java.lang.reflect.Method; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.apache.aries.blueprint.BeanProcessor; |
| import org.apache.aries.blueprint.ExtendedBlueprintContainer; |
| import org.apache.aries.blueprint.ServiceProcessor; |
| import org.apache.aries.blueprint.container.ServiceListener; |
| import org.apache.aries.blueprint.utils.JavaUtils; |
| import org.apache.aries.blueprint.utils.ReflectionUtils; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.blueprint.reflect.ServiceMetadata; |
| import org.osgi.service.cm.Configuration; |
| import org.osgi.service.cm.ConfigurationAdmin; |
| import org.osgi.service.cm.ConfigurationException; |
| import org.osgi.service.cm.ManagedServiceFactory; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * TODO: if we need to make those exported services tied to their references as for other <service/> elements |
| * TODO: it becomes a problem as currently we would have to create a specific recipe or something like that |
| * |
| * @version $Rev$, $Date$ |
| */ |
| public class CmManagedServiceFactory { |
| |
| static final int CONFIGURATION_ADMIN_OBJECT_DELETED = 1; |
| |
| static final int BUNDLE_STOPPING = 2; |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(CmManagedServiceFactory.class); |
| |
| private ExtendedBlueprintContainer blueprintContainer; |
| private ConfigurationAdmin configAdmin; |
| private String id; |
| private String factoryPid; |
| private List<String> interfaces; |
| private int autoExport; |
| private int ranking; |
| private Map serviceProperties; |
| private String managedComponentName; |
| private String componentDestroyMethod; |
| private List<ServiceListener> listeners; |
| private final Object lock = new Object(); |
| |
| private ServiceRegistration registration; |
| private Map<String, ServiceRegistration> pids = new ConcurrentHashMap<String, ServiceRegistration>(); |
| private Map<ServiceRegistration, Object> services = new ConcurrentHashMap<ServiceRegistration, Object>(); |
| |
| public void init() throws Exception { |
| LOGGER.debug("Initializing CmManagedServiceFactory for factoryPid={}", factoryPid); |
| Properties props = new Properties(); |
| props.put(Constants.SERVICE_PID, factoryPid); |
| Bundle bundle = blueprintContainer.getBundleContext().getBundle(); |
| props.put(Constants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName()); |
| props.put(Constants.BUNDLE_VERSION, bundle.getHeaders().get(Constants.BUNDLE_VERSION)); |
| |
| synchronized(lock) { |
| registration = blueprintContainer.getBundleContext().registerService(ManagedServiceFactory.class.getName(), new ConfigurationWatcher(), props); |
| |
| String filter = '(' + ConfigurationAdmin.SERVICE_FACTORYPID + '=' + this.factoryPid + ')'; |
| Configuration[] configs = configAdmin.listConfigurations(filter); |
| if (configs != null) { |
| for (Configuration config : configs) { |
| updated(config.getPid(), config.getProperties()); |
| } |
| } |
| } |
| } |
| |
| public void destroy() { |
| if (registration != null) { |
| registration.unregister(); |
| } |
| for (Map.Entry<ServiceRegistration, Object> entry : services.entrySet()) { |
| destroy(entry.getValue(), entry.getKey(), BUNDLE_STOPPING); |
| } |
| services.clear(); |
| pids.clear(); |
| } |
| |
| private void destroy(Object component, ServiceRegistration registration, int code) { |
| if (listeners != null) { |
| ServiceReference ref = registration.getReference(); |
| for (ServiceListener listener : listeners) { |
| Hashtable props = JavaUtils.getProperties(ref); |
| listener.unregister(component, props); |
| } |
| } |
| destroyComponent(component, code); |
| registration.unregister(); |
| } |
| |
| public Map<ServiceRegistration, Object> getServiceMap() { |
| return Collections.unmodifiableMap(services); |
| } |
| |
| public void setBlueprintContainer(ExtendedBlueprintContainer blueprintContainer) { |
| this.blueprintContainer = blueprintContainer; |
| } |
| |
| public void setConfigAdmin(ConfigurationAdmin configAdmin) { |
| this.configAdmin = configAdmin; |
| } |
| |
| public void setListeners(List<ServiceListener> listeners) { |
| this.listeners = listeners; |
| } |
| |
| public void setId(String id) { |
| this.id = id; |
| } |
| |
| public void setFactoryPid(String factoryPid) { |
| this.factoryPid = factoryPid; |
| } |
| |
| public void setInterfaces(List<String> interfaces) { |
| this.interfaces = interfaces; |
| } |
| |
| public void setAutoExport(int autoExport) { |
| this.autoExport = autoExport; |
| } |
| |
| public void setRanking(int ranking) { |
| this.ranking = ranking; |
| } |
| |
| public void setServiceProperties(Map serviceProperties) { |
| this.serviceProperties = serviceProperties; |
| } |
| |
| public void setManagedComponentName(String managedComponentName) { |
| this.managedComponentName = managedComponentName; |
| } |
| |
| public void setComponentDestroyMethod(String componentDestroyMethod) { |
| this.componentDestroyMethod = componentDestroyMethod; |
| } |
| |
| protected void updated(String pid, Dictionary props) { |
| LOGGER.debug("Updated configuration {} with props {}", pid, props); |
| ServiceRegistration reg = pids.get(pid); |
| if (reg == null) { |
| updateComponentProperties(props); |
| |
| Object component = blueprintContainer.getComponentInstance(managedComponentName); |
| |
| // TODO: call listeners, etc... |
| |
| Hashtable regProps = getRegistrationProperties(pid); |
| CmProperties cm = findServiceProcessor(); |
| if (cm != null) { |
| if ("".equals(cm.getPersistentId())) { |
| JavaUtils.copy(regProps, props); |
| } |
| cm.updateProperties(new PropertiesUpdater(pid), regProps); |
| } |
| |
| Set<String> classes = getClasses(component); |
| String[] classArray = classes.toArray(new String[classes.size()]); |
| reg = blueprintContainer.getBundleContext().registerService(classArray, component, regProps); |
| |
| LOGGER.debug("Service {} registered with interfaces {} and properties {}", new Object [] { component, classes, regProps }); |
| |
| services.put(reg, component); |
| pids.put(pid, reg); |
| |
| if (listeners != null) { |
| for (ServiceListener listener : listeners) { |
| listener.register(component, regProps); |
| } |
| } |
| } else { |
| updateComponentProperties(props); |
| |
| CmProperties cm = findServiceProcessor(); |
| if (cm != null && "".equals(cm.getPersistentId())) { |
| Dictionary regProps = getRegistrationProperties(pid); |
| JavaUtils.copy(regProps, props); |
| cm.updated(regProps); |
| } |
| } |
| } |
| |
| private Hashtable getRegistrationProperties(String pid) { |
| Hashtable regProps = new Hashtable(); |
| if (serviceProperties != null) { |
| regProps.putAll(serviceProperties); |
| } |
| regProps.put(Constants.SERVICE_PID, pid); |
| regProps.put(Constants.SERVICE_RANKING, ranking); |
| return regProps; |
| } |
| |
| private void updateComponentProperties(Dictionary props) { |
| CmManagedProperties cm = findBeanProcessor(); |
| if (cm != null) { |
| cm.updated(props); |
| } |
| } |
| |
| private CmManagedProperties findBeanProcessor() { |
| for (BeanProcessor beanProcessor : blueprintContainer.getProcessors(BeanProcessor.class)) { |
| if (beanProcessor instanceof CmManagedProperties) { |
| CmManagedProperties cm = (CmManagedProperties) beanProcessor; |
| if (managedComponentName.equals(cm.getBeanName()) && "".equals(cm.getPersistentId())) { |
| return cm; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private CmProperties findServiceProcessor() { |
| for (ServiceProcessor processor : blueprintContainer.getProcessors(ServiceProcessor.class)) { |
| if (processor instanceof CmProperties) { |
| CmProperties cm = (CmProperties) processor; |
| if (id.equals(cm.getServiceId())) { |
| return cm; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void destroyComponent(Object instance, int reason) { |
| Method method = findDestroyMethod(instance.getClass()); |
| if (method != null) { |
| try { |
| method.invoke(instance, new Object [] { reason }); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private Method findDestroyMethod(Class clazz) { |
| Method method = null; |
| if (componentDestroyMethod != null && componentDestroyMethod.length() > 0) { |
| List<Method> methods = ReflectionUtils.findCompatibleMethods(clazz, componentDestroyMethod, new Class [] { int.class }); |
| if (methods != null && !methods.isEmpty()) { |
| method = methods.get(0); |
| } |
| } |
| return method; |
| } |
| |
| protected void deleted(String pid) { |
| LOGGER.debug("Deleted configuration {}", pid); |
| ServiceRegistration reg = pids.remove(pid); |
| if (reg != null) { |
| Object component = services.remove(reg); |
| destroy(component, reg, CONFIGURATION_ADMIN_OBJECT_DELETED); |
| } |
| } |
| |
| private Set<String> getClasses(Object service) { |
| Class serviceClass = service.getClass(); |
| Set<String> classes; |
| switch (autoExport) { |
| case ServiceMetadata.AUTO_EXPORT_INTERFACES: |
| classes = ReflectionUtils.getImplementedInterfaces(new HashSet<String>(), serviceClass); |
| break; |
| case ServiceMetadata.AUTO_EXPORT_CLASS_HIERARCHY: |
| classes = ReflectionUtils.getSuperClasses(new HashSet<String>(), serviceClass); |
| break; |
| case ServiceMetadata.AUTO_EXPORT_ALL_CLASSES: |
| classes = ReflectionUtils.getSuperClasses(new HashSet<String>(), serviceClass); |
| classes = ReflectionUtils.getImplementedInterfaces(classes, serviceClass); |
| break; |
| default: |
| classes = new HashSet<String>(interfaces); |
| break; |
| } |
| return classes; |
| } |
| |
| private class ConfigurationWatcher implements ManagedServiceFactory { |
| |
| public String getName() { |
| return null; |
| } |
| |
| public void updated(String pid, Dictionary props) throws ConfigurationException { |
| CmManagedServiceFactory.this.updated(pid, props); |
| } |
| |
| public void deleted(String pid) { |
| CmManagedServiceFactory.this.deleted(pid); |
| } |
| } |
| |
| private class PropertiesUpdater implements ServiceProcessor.ServicePropertiesUpdater { |
| |
| private String pid; |
| |
| public PropertiesUpdater(String pid) { |
| this.pid = pid; |
| } |
| |
| public String getId() { |
| return id; |
| } |
| |
| public void updateProperties(Dictionary properties) { |
| ServiceRegistration reg = pids.get(pid); |
| if (reg != null) { |
| ServiceReference ref = reg.getReference(); |
| if (ref != null) { |
| Hashtable table = JavaUtils.getProperties(ref); |
| JavaUtils.copy(table, properties); |
| reg.setProperties(table); |
| } |
| } |
| } |
| } |
| |
| } |