/* * 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.felix.ipojo.composite.service.instantiator; import java.util.ArrayList; import java.util.Comparator; import java.util.Dictionary; import java.util.Enumeration; import java.util.List; import java.util.Properties; import org.apache.felix.ipojo.ComponentInstance; import org.apache.felix.ipojo.ConfigurationException; import org.apache.felix.ipojo.PolicyServiceContext; import org.apache.felix.ipojo.architecture.HandlerDescription; import org.apache.felix.ipojo.composite.CompositeHandler; import org.apache.felix.ipojo.composite.instance.InstanceHandler; import org.apache.felix.ipojo.composite.util.SourceManager; import org.apache.felix.ipojo.metadata.Element; import org.apache.felix.ipojo.parser.ParseException; import org.apache.felix.ipojo.util.DependencyMetadataHelper; import org.apache.felix.ipojo.util.DependencyModel; import org.apache.felix.ipojo.util.DependencyStateListener; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; /** * Service Instantiator Class. This handler allows to instantiate service * instance inside the composition. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class ServiceDependencyHandler extends CompositeHandler implements DependencyStateListener { /** * List of instances to manage. */ private List/* <SvcInstance> */m_instances = new ArrayList(); /** * List of importers. */ private List/* <ServiceImporter> */ m_importers = new ArrayList(); /** * Flag indicating if the handler has already finished the start method. */ private boolean m_isStarted; /** * The handler description. */ private ServiceInstantiatorDescription m_description; /** * Source Managers. */ private List m_sources; /** * Create a Service instance object form the given Element. * This method parse the given element and configure the service instance object. * @param service : the Element describing the service instance * @param conf : the configuration from the composite instance * @throws ConfigurationException : the service instance cannot be created correctly */ private void createServiceInstance(Element service, Dictionary conf) throws ConfigurationException { // Prepare the configuration to append. Properties toAppend = new Properties(); Enumeration keys = conf.keys(); while(keys.hasMoreElements()) { String key = (String) keys.nextElement(); if (! (key.equals("instance.name") || key.equals("component"))) { // Remove instance.name and component toAppend.put(key, conf.get(key)); } } String spec = service.getAttribute("specification"); if (spec == null) { throw new ConfigurationException("Malformed service : the specification attribute is mandatory"); } String filter = "(&(!(factory.name=" + getCompositeManager().getFactory().getComponentDescription().getName() + "))(factory.state=1))"; // Cannot reinstantiate yourself String givenFilter = service.getAttribute("filter"); if (givenFilter != null) { filter = "(&" + filter + givenFilter + ")"; //NOPMD } Filter fil; try { fil = getCompositeManager().getGlobalContext().createFilter(filter); } catch (InvalidSyntaxException e) { throw new ConfigurationException("Malformed filter " + filter, e); } Properties prop = new Properties(); Element[] props = service.getElements("property"); for (int k = 0; props != null && k < props.length; k++) { try { InstanceHandler.parseProperty(props[k], prop); } catch (ParseException e) { throw new ConfigurationException("An instance configuration is invalid", e); } } Properties instanceConfiguration = new Properties(); instanceConfiguration.putAll(prop); instanceConfiguration.putAll(toAppend); String aggregate = service.getAttribute("aggregate"); boolean agg = aggregate != null && aggregate.equalsIgnoreCase("true"); String optional = service.getAttribute("optional"); boolean opt = optional != null && optional.equalsIgnoreCase("true"); int policy = DependencyMetadataHelper.getPolicy(service); Comparator cmp = DependencyMetadataHelper.getComparator(service, getCompositeManager().getGlobalContext()); SvcInstance inst = new SvcInstance(this, spec, instanceConfiguration, agg, opt, fil, cmp, policy); m_instances.add(inst); String sources = service.getAttribute("context-source"); if (sources != null) { SourceManager source = new SourceManager(sources, filter, inst, getCompositeManager()); if (m_sources == null) { m_sources = new ArrayList(1); } m_sources.add(source); } } /** * Create a Service importer object from the given Element. * This method parse the given element and configure the service importer object. * @param imp : Element describing the import * @param confFilter : instance filter customization * @throws ConfigurationException : the service importer cannot be created correctly */ private void createServiceImport(Element imp, Dictionary confFilter) throws ConfigurationException { boolean optional = false; boolean aggregate = false; String specification = imp.getAttribute("specification"); if (specification == null) { // Malformed import error("Malformed import: the specification attribute is mandatory"); throw new ConfigurationException("Malformed import : the specification attribute is mandatory"); } else { String opt = imp.getAttribute("optional"); optional = opt != null && opt.equalsIgnoreCase("true"); String agg = imp.getAttribute("aggregate"); aggregate = agg != null && agg.equalsIgnoreCase("true"); String original = "(&(objectClass=" + specification + ")(!(instance.name=" + getCompositeManager().getInstanceName() + ")))"; // Cannot import yourself String filter = original; String givenFilter = imp.getAttribute("filter"); if (givenFilter != null) { filter = "(&" + filter + givenFilter + ")"; //NOPMD } String identitity = imp.getAttribute("id"); String scope = imp.getAttribute("scope"); BundleContext context = getCompositeManager().getGlobalContext(); // Get the default bundle context. if (scope != null) { if (scope.equalsIgnoreCase("global")) { context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.GLOBAL); } else if (scope.equalsIgnoreCase("composite")) { context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL); } else if (scope.equalsIgnoreCase("composite+global")) { context = new PolicyServiceContext(getCompositeManager().getGlobalContext(), getCompositeManager().getParentServiceContext(), PolicyServiceContext.LOCAL_AND_GLOBAL); } } // Configure instance filter if available if (confFilter != null && identitity != null && confFilter.get(identitity) != null) { filter = "(&" + original + (String) confFilter.get(identitity) + ")"; } Filter fil = null; if (filter != null) { try { fil = getCompositeManager().getGlobalContext().createFilter(filter); } catch (InvalidSyntaxException e) { throw new ConfigurationException("A required filter " + filter + " is malformed", e); } } Comparator cmp = DependencyMetadataHelper.getComparator(imp, getCompositeManager().getGlobalContext()); Class spec = DependencyMetadataHelper.loadSpecification(specification, getCompositeManager().getGlobalContext()); int policy = DependencyMetadataHelper.getPolicy(imp); ServiceImporter importer = new ServiceImporter(spec, fil, aggregate, optional, cmp, policy, context, identitity, this); m_importers.add(importer); String sources = imp.getAttribute("context-source"); if (sources != null) { SourceManager source = new SourceManager(sources, filter, importer, getCompositeManager()); if (m_sources == null) { m_sources = new ArrayList(1); } m_sources.add(source); } } } /** * Configure the handler. * @param metadata : the metadata of the component * @param conf : the instance configuration * @throws ConfigurationException : the specification attribute is missing * @see CompositeHandler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary) */ public void configure(Element metadata, Dictionary conf) throws ConfigurationException { Element[] services = metadata.getElements("subservice"); // Get instance filters Dictionary confFilter = null; if (conf.get("requires.filters") != null) { confFilter = (Dictionary) conf.get("requires.filters"); } for (int i = 0; i < services.length; i++) { String action = services[i].getAttribute("action"); if (action == null) { throw new ConfigurationException("The action attribute must be set to 'instantiate' or 'import'"); } else if ("instantiate".equalsIgnoreCase(action)) { createServiceInstance(services[i], conf); } else if ("import".equalsIgnoreCase(action)) { createServiceImport(services[i], confFilter); } else { throw new ConfigurationException("Unknown action : " + action); } } m_description = new ServiceInstantiatorDescription(this, m_instances, m_importers); } /** * Start the service instantiator handler. * Start all created service instance. * @see org.apache.felix.ipojo.composite.CompositeHandler#start() */ public void start() { for (int i = 0; m_sources != null && i < m_sources.size(); i++) { SourceManager source = (SourceManager) m_sources.get(i); source.start(); } for (int i = 0; i < m_importers.size(); i++) { ServiceImporter imp = (ServiceImporter) m_importers.get(i); imp.start(); } for (int i = 0; i < m_instances.size(); i++) { SvcInstance inst = (SvcInstance) m_instances.get(i); inst.start(); } isHandlerValid(); m_isStarted = true; } /** * Check the handler validity. * @see org.apache.felix.ipojo.composite.CompositeHandler#isValid() */ private void isHandlerValid() { for (int i = 0; i < m_importers.size(); i++) { ServiceImporter imp = (ServiceImporter) m_importers.get(i); if (imp.getState() != DependencyModel.RESOLVED) { setValidity(false); return; } } for (int i = 0; i < m_instances.size(); i++) { SvcInstance inst = (SvcInstance) m_instances.get(i); if (inst.getState() != DependencyModel.RESOLVED) { setValidity(false); return; } } setValidity(true); } /** * Handler stop method. * Stop all created service instance. * @see org.apache.felix.ipojo.composite.CompositeHandler#stop() */ public void stop() { for (int i = 0; m_sources != null && i < m_sources.size(); i++) { SourceManager source = (SourceManager) m_sources.get(i); source.stop(); } for (int i = 0; i < m_instances.size(); i++) { SvcInstance inst = (SvcInstance) m_instances.get(i); inst.stop(); } for (int i = 0; i < m_importers.size(); i++) { ServiceImporter imp = (ServiceImporter) m_importers.get(i); imp.stop(); } m_isStarted = false; } /** * State change callback. * This method is used to freeze the set of used provider if the static binding policy is used. * @param newState : the new state of the underlying instance * @see org.apache.felix.ipojo.Handler#stateChanged(int) */ public void stateChanged(int newState) { // If we are becoming valid and started, check if we need to freeze importers. if (m_isStarted && newState == ComponentInstance.VALID) { for (int i = 0; i < m_importers.size(); i++) { ServiceImporter imp = (ServiceImporter) m_importers.get(i); if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) { imp.freeze(); } } for (int i = 0; i < m_instances.size(); i++) { SvcInstance imp = (SvcInstance) m_instances.get(i); if (imp.getBindingPolicy() == DependencyModel.STATIC_BINDING_POLICY) { imp.freeze(); } } } } /** * An service instance becomes valid. * @param dep : dependency becoming valid. */ public void validate(DependencyModel dep) { if (!getValidity()) { isHandlerValid(); } } /** * A service instance becomes invalid. * @param dep : dependency becoming valid. */ public void invalidate(DependencyModel dep) { if (getValidity()) { isHandlerValid(); } } /** * Get the service instantiator handler description. * @return the description * @see org.apache.felix.ipojo.composite.CompositeHandler#getDescription() */ public HandlerDescription getDescription() { return m_description; } public List getInstances() { return m_instances; } public List getRequirements() { return m_importers; } }