/* | |
* 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; | |
} | |
} |