From 83c5bbb865803e0a06e0c24e9de849af10bb4993 Mon Sep 17 00:00:00 2001 From: Tom Pantelis Date: Sun, 20 Mar 2016 00:06:08 -0400 Subject: [PATCH] Add decorating "type" attr extension for service refs Added an attribute extension to the element that adds an OSGi service property of the form "type=". This allows providers of a service to advertise the type of the service to distinguish it from other services provided for the same interface. Conversely, the extension can also be used in elements to allow consumers of the service to specify which service implementation they want. As a convention, the "default" type should be used for the default implementation of the service with other types being specialized implementations. This allows consumers to obtain whichever implementation the provider deems as the default without having to explicitly know it and allows the provider to switch to a new implementation without requiring all consumers to change their referenced type. If a consumer doesn't specify the "type" attribute, it may be ambiguous as to which service is obtained. Rather than requiring that the "type" attribute be specfied for every element, I added an attribute extension attribute, "use-default-for-reference-types", to the element that automatically adds a filter to all elements where the "type" property is either not set or set to "default" if the "type" attribute isn't explicitly specified. This ensures the default implementation is imported if there are other implementations advertised with other types. Change-Id: Ie61bb45da1c7539732cd31ab0f8130233c9696fc Signed-off-by: Tom Pantelis --- .../blueprint/ext/ComponentProcessor.java | 43 +++++++++-- .../ext/OpendaylightNamespaceHandler.java | 76 ++++++++++++++++++- .../opendaylight-blueprint-ext-1.0.0.xsd | 2 + 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ComponentProcessor.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ComponentProcessor.java index ed87e7093b..bae06b1ea6 100644 --- a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ComponentProcessor.java +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ComponentProcessor.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.blueprint.ext; +import com.google.common.base.Strings; import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; @@ -15,6 +16,7 @@ import org.apache.aries.blueprint.ComponentDefinitionRegistry; import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor; import org.apache.aries.blueprint.ext.AbstractPropertyPlaceholder; import org.apache.aries.blueprint.mutable.MutableBeanMetadata; +import org.apache.aries.blueprint.mutable.MutableServiceReferenceMetadata; import org.apache.aries.util.AriesFrameworkUtil; import org.opendaylight.controller.blueprint.BlueprintContainerRestartService; import org.osgi.framework.Bundle; @@ -41,6 +43,7 @@ public class ComponentProcessor implements ComponentDefinitionRegistryProcessor private Bundle bundle; private BlueprintContainerRestartService blueprintContainerRestartService; private boolean restartDependentsOnUpdates; + private boolean useDefaultForReferenceTypes; public void setBundle(Bundle bundle) { this.bundle = bundle; @@ -54,6 +57,10 @@ public class ComponentProcessor implements ComponentDefinitionRegistryProcessor this.restartDependentsOnUpdates = restartDependentsOnUpdates; } + public void setUseDefaultForReferenceTypes(boolean useDefaultForReferenceTypes) { + this.useDefaultForReferenceTypes = useDefaultForReferenceTypes; + } + public void destroy() { for(ServiceRegistration reg: managedServiceRegs) { AriesFrameworkUtil.safeUnregisterService(reg); @@ -62,20 +69,42 @@ public class ComponentProcessor implements ComponentDefinitionRegistryProcessor @Override public void process(ComponentDefinitionRegistry registry) { - LOG.debug("{}: In process", bundle.getSymbolicName()); + LOG.debug("{}: In process", logName()); for(String name : registry.getComponentDefinitionNames()) { ComponentMetadata component = registry.getComponentDefinition(name); if(component instanceof MutableBeanMetadata) { processMutableBeanMetadata((MutableBeanMetadata)component); + } else if(component instanceof MutableServiceReferenceMetadata) { + processServiceReferenceMetadata((MutableServiceReferenceMetadata)component); } } } + private void processServiceReferenceMetadata(MutableServiceReferenceMetadata serviceRef) { + if(!useDefaultForReferenceTypes) { + return; + } + + String filter = serviceRef.getFilter(); + String extFilter = serviceRef.getExtendedFilter() == null ? null : + serviceRef.getExtendedFilter().getStringValue(); + + LOG.debug("{}: processServiceReferenceMetadata for {}, filter: {}, ext filter: {}", logName(), + serviceRef.getId(), filter, extFilter); + + if(Strings.isNullOrEmpty(filter) && Strings.isNullOrEmpty(extFilter)) { + serviceRef.setFilter("(|(type=default)(!(type=*)))"); + + LOG.debug("{}: processServiceReferenceMetadata for {} set filter to {}", logName(), + serviceRef.getId(), serviceRef.getFilter()); + } + } + private void processMutableBeanMetadata(MutableBeanMetadata bean) { if(restartDependentsOnUpdates && bean.getRuntimeClass() != null && AbstractPropertyPlaceholder.class.isAssignableFrom(bean.getRuntimeClass())) { - LOG.debug("{}: Found PropertyPlaceholder bean: {}, runtime {}", bundle.getSymbolicName(), bean.getId(), + LOG.debug("{}: Found PropertyPlaceholder bean: {}, runtime {}", logName(), bean.getId(), bean.getRuntimeClass()); for(BeanProperty prop: bean.getProperties()) { @@ -83,13 +112,13 @@ public class ComponentProcessor implements ComponentDefinitionRegistryProcessor if(prop.getValue() instanceof ValueMetadata) { ValueMetadata persistentId = (ValueMetadata)prop.getValue(); - LOG.debug("{}: Found {} property, value : {}", bundle.getSymbolicName(), + LOG.debug("{}: Found {} property, value : {}", logName(), CM_PERSISTENT_ID_PROPERTY, persistentId.getStringValue()); registerManagedService(persistentId.getStringValue()); } else { LOG.debug("{}: {} property metadata {} is not instanceof ValueMetadata", - bundle.getSymbolicName(), CM_PERSISTENT_ID_PROPERTY, prop.getValue()); + logName(), CM_PERSISTENT_ID_PROPERTY, prop.getValue()); } break; @@ -107,7 +136,7 @@ public class ComponentProcessor implements ComponentDefinitionRegistryProcessor @Override public void updated(Dictionary properties) { LOG.debug("{}: ManagedService updated for persistentId {}, properties: {}, initialUpdate: {}", - bundle.getSymbolicName(), persistentId, properties, initialUpdate); + logName(), persistentId, properties, initialUpdate); // The first update occurs when the service is registered so ignore it as we want subsequent // updates when it changes. The ConfigAdmin will send an update even if the cfg file doesn't @@ -127,4 +156,8 @@ public class ComponentProcessor implements ComponentDefinitionRegistryProcessor managedServiceRegs.add(bundle.getBundleContext().registerService(ManagedService.class.getName(), managedService, props)); } + + private String logName() { + return bundle.getSymbolicName(); + } } diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/OpendaylightNamespaceHandler.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/OpendaylightNamespaceHandler.java index 89bea93182..0b361263f6 100644 --- a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/OpendaylightNamespaceHandler.java +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/OpendaylightNamespaceHandler.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.blueprint.ext; +import com.google.common.base.Strings; import java.net.URL; import java.util.Collections; import java.util.Set; @@ -16,6 +17,8 @@ import org.apache.aries.blueprint.ParserContext; import org.apache.aries.blueprint.mutable.MutableBeanMetadata; import org.apache.aries.blueprint.mutable.MutableRefMetadata; import org.apache.aries.blueprint.mutable.MutableReferenceMetadata; +import org.apache.aries.blueprint.mutable.MutableServiceMetadata; +import org.apache.aries.blueprint.mutable.MutableServiceReferenceMetadata; import org.apache.aries.blueprint.mutable.MutableValueMetadata; import org.opendaylight.controller.blueprint.BlueprintContainerRestartService; import org.osgi.service.blueprint.container.ComponentDefinitionException; @@ -24,6 +27,8 @@ import org.osgi.service.blueprint.reflect.ComponentMetadata; import org.osgi.service.blueprint.reflect.Metadata; import org.osgi.service.blueprint.reflect.RefMetadata; import org.osgi.service.blueprint.reflect.ReferenceMetadata; +import org.osgi.service.blueprint.reflect.ServiceMetadata; +import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata; import org.osgi.service.blueprint.reflect.ValueMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +47,8 @@ public class OpendaylightNamespaceHandler implements NamespaceHandler { private static final Logger LOG = LoggerFactory.getLogger(OpendaylightNamespaceHandler.class); private static final String COMPONENT_PROCESSOR_NAME = ComponentProcessor.class.getName(); private static final String RESTART_DEPENDENTS_ON_UPDATES = "restart-dependents-on-updates"; + private static final String USE_DEFAULT_FOR_REFERENCE_TYPES = "use-default-for-reference-types"; + private static final String TYPE_ATTR = "type"; @SuppressWarnings("rawtypes") @Override @@ -71,6 +78,17 @@ public class OpendaylightNamespaceHandler implements NamespaceHandler { if(node instanceof Attr) { if (nodeNameEquals(node, RESTART_DEPENDENTS_ON_UPDATES)) { return decorateRestartDependentsOnUpdates((Attr)node, component, context); + } else if (nodeNameEquals(node, USE_DEFAULT_FOR_REFERENCE_TYPES)) { + return decorateUseDefaultForReferenceTypes((Attr)node, component, context); + } else if (nodeNameEquals(node, TYPE_ATTR)) { + if(component instanceof ServiceReferenceMetadata) { + return decorateServiceReferenceType((Attr)node, component, context); + } else if(component instanceof ServiceMetadata) { + return decorateServiceType((Attr)node, component, context); + } + + throw new ComponentDefinitionException("Attribute " + node.getNodeName() + + " can only be used on a , or element"); } throw new ComponentDefinitionException("Unsupported attribute: " + node.getNodeName()); @@ -79,21 +97,75 @@ public class OpendaylightNamespaceHandler implements NamespaceHandler { } } + private ComponentMetadata decorateServiceType(Attr attr, ComponentMetadata component, ParserContext context) { + if (!(component instanceof MutableServiceMetadata)) { + throw new ComponentDefinitionException("Expected an instanceof MutableServiceMetadata"); + } + + MutableServiceMetadata service = (MutableServiceMetadata)component; + + LOG.debug("decorateServiceType for {} - adding type property {}", service.getId(), attr.getValue()); + + service.addServiceProperty(createValue(context, TYPE_ATTR), createValue(context, attr.getValue())); + return component; + } + + private ComponentMetadata decorateServiceReferenceType(Attr attr, ComponentMetadata component, ParserContext context) { + if (!(component instanceof MutableServiceReferenceMetadata)) { + throw new ComponentDefinitionException("Expected an instanceof MutableServiceReferenceMetadata"); + } + + // We don't actually need the ComponentProcessor for augmenting the OSGi filter here but we create it + // to workaround an issue in Aries where it doesn't use the extended filter unless there's a + // Processor or ComponentDefinitionRegistryProcessor registered. This may actually be working as + // designed in Aries b/c the extended filter was really added to allow the OSGi filter to be + // substituted by a variable via the "cm:property-placeholder" processor. If so, it's a bit funky + // but as long as there's at least one processor registered, it correctly uses the extended filter. + registerComponentProcessor(context); + + MutableServiceReferenceMetadata serviceRef = (MutableServiceReferenceMetadata)component; + String oldFilter = serviceRef.getExtendedFilter() == null ? null : + serviceRef.getExtendedFilter().getStringValue(); + + String filter; + if(Strings.isNullOrEmpty(oldFilter)) { + filter = String.format("(type=%s)", attr.getValue()); + } else { + filter = String.format("(&(%s)(type=%s))", oldFilter, attr.getValue()); + } + + LOG.debug("decorateServiceReferenceType for {} with type {}, old filter: {}, new filter: {}", + serviceRef.getId(), attr.getValue(), oldFilter, filter); + + serviceRef.setExtendedFilter(createValue(context, filter)); + return component; + } + private static ComponentMetadata decorateRestartDependentsOnUpdates(Attr attr, ComponentMetadata component, ParserContext context) { + return enableComponentProcessorProperty(attr, component, context, "restartDependentsOnUpdates"); + } + + private static ComponentMetadata decorateUseDefaultForReferenceTypes(Attr attr, ComponentMetadata component, + ParserContext context) { + return enableComponentProcessorProperty(attr, component, context, "useDefaultForReferenceTypes"); + } + + private static ComponentMetadata enableComponentProcessorProperty(Attr attr, ComponentMetadata component, + ParserContext context, String propertyName) { if(component != null) { throw new ComponentDefinitionException("Attribute " + attr.getNodeName() + " can only be used on the root element"); } - LOG.debug("decorateRestartDependentsOnUpdates: {}", attr.getValue()); + LOG.debug("{}: {}", propertyName, attr.getValue()); if(!Boolean.TRUE.equals(Boolean.valueOf(attr.getValue()))) { return component; } MutableBeanMetadata metadata = registerComponentProcessor(context); - metadata.addProperty("restartDependentsOnUpdates", createValue(context, "true")); + metadata.addProperty(propertyName, createValue(context, "true")); return component; } diff --git a/opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-1.0.0.xsd b/opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-1.0.0.xsd index f83434f563..088e2fc5e3 100644 --- a/opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-1.0.0.xsd +++ b/opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-1.0.0.xsd @@ -6,5 +6,7 @@ + + \ No newline at end of file -- 2.36.6