From d98d5bca742bfdff6c23e27aca21da04b15c33dd Mon Sep 17 00:00:00 2001 From: Tom Pantelis Date: Mon, 21 Mar 2016 04:41:47 -0400 Subject: [PATCH] Add blueprint extensions to get and register RPC services Added several blueprint extension elements to support RPCs: - registers a global RPC implementation. If "interface" isn't specified, it registers all RpcService interfaces implemented by the ref'ed instance. - registers a routed RPC implementation and returns a RoutedRpcRegistration instance for injection into other beans via the specified "id. If "interface" isn't specified, it looks for a single RpcService interface implemented by the ref'ed instance. If multiple are found it fails since only one RoutedRpcRegistration instance can be returned. - finds the registered RpcService corresponding to the specified "interface" and makes it available for injection into other beans via the specified "id". Internally the bean implementations obtain the binding RpcServiceRegistry. Change-Id: I432dfb5378ca8368e41fb5375c9d5515dd3e714d Signed-off-by: Tom Pantelis --- opendaylight/blueprint/pom.xml | 4 + .../ext/OpendaylightNamespaceHandler.java | 96 ++++++++++++++- .../blueprint/ext/RoutedRpcMetadata.java | 115 ++++++++++++++++++ .../ext/RoutedRpcRegistrationConverter.java | 34 ++++++ .../blueprint/ext/RpcImplementationBean.java | 109 +++++++++++++++++ .../blueprint/ext/RpcServiceMetadata.java | 100 +++++++++++++++ .../opendaylight-blueprint-ext-1.0.0.xsd | 21 +++- 7 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcMetadata.java create mode 100644 opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcRegistrationConverter.java create mode 100644 opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcImplementationBean.java create mode 100644 opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcServiceMetadata.java diff --git a/opendaylight/blueprint/pom.xml b/opendaylight/blueprint/pom.xml index 511a185cd4..fc033947b2 100644 --- a/opendaylight/blueprint/pom.xml +++ b/opendaylight/blueprint/pom.xml @@ -24,6 +24,10 @@ org.apache.aries.blueprint.core 1.4.2 + + org.opendaylight.controller + sal-binding-api + org.osgi org.osgi.core 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 0b361263f6..bafc1f7a73 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 @@ -14,6 +14,7 @@ import java.util.Set; import org.apache.aries.blueprint.ComponentDefinitionRegistry; import org.apache.aries.blueprint.NamespaceHandler; import org.apache.aries.blueprint.ParserContext; +import org.apache.aries.blueprint.ext.ComponentFactoryMetadata; import org.apache.aries.blueprint.mutable.MutableBeanMetadata; import org.apache.aries.blueprint.mutable.MutableRefMetadata; import org.apache.aries.blueprint.mutable.MutableReferenceMetadata; @@ -21,6 +22,7 @@ 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.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.osgi.service.blueprint.container.ComponentDefinitionException; import org.osgi.service.blueprint.reflect.BeanMetadata; import org.osgi.service.blueprint.reflect.ComponentMetadata; @@ -43,12 +45,18 @@ import org.w3c.dom.Node; */ public class OpendaylightNamespaceHandler implements NamespaceHandler { public static final String NAMESPACE_1_0_0 = "http://opendaylight.org/xmlns/blueprint/v1.0.0"; + static final String ROUTED_RPC_REG_CONVERTER_NAME = "org.opendaylight.blueprint.RoutedRpcRegConverter"; + static final String RPC_REGISTRY_NAME = "org.opendaylight.blueprint.RpcRegistry"; 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"; + private static final String INTERFACE = "interface"; + private static final String REF_ATTR = "ref"; + private static final String ID_ATTR = "id"; + private static final String RPC_SERVICE = "rpc-service"; @SuppressWarnings("rawtypes") @Override @@ -70,6 +78,15 @@ public class OpendaylightNamespaceHandler implements NamespaceHandler { @Override public Metadata parse(Element element, ParserContext context) { LOG.debug("In parse for {}", element); + + if (nodeNameEquals(element, RpcImplementationBean.RPC_IMPLEMENTATION)) { + return parseRpcImplementation(element, context); + } else if (nodeNameEquals(element, RoutedRpcMetadata.ROUTED_RPC_IMPLEMENTATION)) { + return parseRoutedRpcImplementation(element, context); + } else if (nodeNameEquals(element, RPC_SERVICE)) { + return parseRpcService(element, context); + } + throw new ComponentDefinitionException("Unsupported standalone element: " + element.getNodeName()); } @@ -193,6 +210,73 @@ public class OpendaylightNamespaceHandler implements NamespaceHandler { return metadata; } + private Metadata parseRpcImplementation(Element element, ParserContext context) { + registerRpcRegistryServiceRefBean(context); + + MutableBeanMetadata metadata = context.createMetadata(MutableBeanMetadata.class); + metadata.setId(context.generateId()); + metadata.setScope(BeanMetadata.SCOPE_SINGLETON); + metadata.setActivation(ReferenceMetadata.ACTIVATION_EAGER); + metadata.setRuntimeClass(RpcImplementationBean.class); + metadata.setInitMethod("init"); + metadata.setDestroyMethod("destroy"); + metadata.addProperty("bundle", createRef(context, "blueprintBundle")); + metadata.addProperty("rpcRegistry", createRef(context, RPC_REGISTRY_NAME)); + metadata.addProperty("implementation", createRef(context, element.getAttribute(REF_ATTR))); + + if(element.hasAttribute(INTERFACE)) { + metadata.addProperty("interfaceName", createValue(context, element.getAttribute(INTERFACE))); + } + + LOG.debug("parseAddRpcImplementation returning {}", metadata); + + return metadata; + } + + private Metadata parseRoutedRpcImplementation(Element element, ParserContext context) { + registerRpcRegistryServiceRefBean(context); + registerRoutedRpcRegistrationConverter(context); + + ComponentFactoryMetadata metadata = new RoutedRpcMetadata(getId(context, element), + element.getAttribute(INTERFACE), element.getAttribute(REF_ATTR)); + + LOG.debug("parseRoutedRpcImplementation returning {}", metadata); + + return metadata; + } + + private Metadata parseRpcService(Element element, ParserContext context) { + registerRpcRegistryServiceRefBean(context); + + ComponentFactoryMetadata metadata = new RpcServiceMetadata(getId(context, element), + element.getAttribute(INTERFACE)); + + LOG.debug("parseRpcService returning {}", metadata); + + return metadata; + } + + private void registerRoutedRpcRegistrationConverter(ParserContext context) { + ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry(); + if(registry.getComponentDefinition(ROUTED_RPC_REG_CONVERTER_NAME) == null) { + MutableBeanMetadata metadata = context.createMetadata(MutableBeanMetadata.class); + metadata.setId(ROUTED_RPC_REG_CONVERTER_NAME); + metadata.setScope(BeanMetadata.SCOPE_SINGLETON); + metadata.setActivation(ReferenceMetadata.ACTIVATION_LAZY); + metadata.setRuntimeClass(RoutedRpcRegistrationConverter.class); + registry.registerTypeConverter(metadata); + } + } + + private void registerRpcRegistryServiceRefBean(ParserContext context) { + ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry(); + if(registry.getComponentDefinition(RPC_REGISTRY_NAME) == null) { + MutableReferenceMetadata metadata = createServiceRef(context, RpcProviderRegistry.class, null); + metadata.setId(RPC_REGISTRY_NAME); + registry.registerComponentDefinition(metadata); + } + } + private static ValueMetadata createValue(ParserContext context, String value) { MutableValueMetadata m = context.createMetadata(MutableValueMetadata.class); m.setStringValue(value); @@ -213,12 +297,20 @@ public class OpendaylightNamespaceHandler implements NamespaceHandler { return m; } - private static RefMetadata createRef(ParserContext context, String value) { + private static RefMetadata createRef(ParserContext context, String id) { MutableRefMetadata metadata = context.createMetadata(MutableRefMetadata.class); - metadata.setComponentId(value); + metadata.setComponentId(id); return metadata; } + private static String getId(ParserContext context, Element element) { + if(element.hasAttribute(ID_ATTR)) { + return element.getAttribute(ID_ATTR); + } else { + return context.generateId(); + } + } + private static boolean nodeNameEquals(Node node, String name) { return name.equals(node.getNodeName()) || name.equals(node.getLocalName()); } diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcMetadata.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcMetadata.java new file mode 100644 index 0000000000..4b71d92e57 --- /dev/null +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcMetadata.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.blueprint.ext; + +import java.util.Arrays; +import java.util.List; +import org.apache.aries.blueprint.ext.ComponentFactoryMetadata; +import org.apache.aries.blueprint.services.ExtendedBlueprintContainer; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory metadata corresponding to the "routed-rpc-implementation" element that registers an RPC + * implementation with the RpcProviderRegistry and provides the RoutedRpcRegistration instance to the + * Blueprint container. + * + * @author Thomas Pantelis + */ +class RoutedRpcMetadata implements ComponentFactoryMetadata { + private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcMetadata.class); + static final String ROUTED_RPC_IMPLEMENTATION = "routed-rpc-implementation"; + + private final String id; + private final String interfaceName; + private final String implementationRefId; + private ExtendedBlueprintContainer container; + + RoutedRpcMetadata(String id, String interfaceName, String implementationRefId) { + this.id = id; + this.interfaceName = interfaceName; + this.implementationRefId = implementationRefId; + } + + @Override + public String getId() { + return id; + } + + @Override + public int getActivation() { + return ACTIVATION_LAZY; + } + + @Override + public List getDependsOn() { + return Arrays.asList(OpendaylightNamespaceHandler.RPC_REGISTRY_NAME, implementationRefId); + } + + @Override + public void init(ExtendedBlueprintContainer container) { + this.container = container; + + LOG.debug("{}: In init", logName()); + } + + @Override + public Object create() throws ComponentDefinitionException { + RpcProviderRegistry rpcRegistry = (RpcProviderRegistry) container.getComponentInstance( + OpendaylightNamespaceHandler.RPC_REGISTRY_NAME); + + Object implementation = container.getComponentInstance(implementationRefId); + + try { + if(!RpcService.class.isAssignableFrom(implementation.getClass())) { + throw new ComponentDefinitionException(String.format( + "Implementation \"ref\" instance %s for \"%s\" is not an RpcService", + implementation.getClass(), ROUTED_RPC_IMPLEMENTATION)); + } + + List> rpcInterfaces = RpcImplementationBean.getImplementedRpcServiceInterfaces( + interfaceName, implementation.getClass(), container.getBundleContext().getBundle(), + ROUTED_RPC_IMPLEMENTATION); + + if(rpcInterfaces.size() > 1) { + throw new ComponentDefinitionException(String.format( + "Implementation \"ref\" instance %s for \"%s\" implements more than one RpcService " + + "interface (%s). Please specify the exact \"interface\"", implementation.getClass(), + ROUTED_RPC_IMPLEMENTATION, rpcInterfaces)); + } + + Class rpcInterface = rpcInterfaces.iterator().next(); + + LOG.debug("{}: create - adding routed implementation {} for RpcService {}", logName(), + implementation, rpcInterface); + + return rpcRegistry.addRoutedRpcImplementation(rpcInterface, (RpcService)implementation); + } catch(ComponentDefinitionException e) { + throw e; + } catch(Exception e) { + throw new ComponentDefinitionException(String.format( + "Error processing \"%s\" for %s", ROUTED_RPC_IMPLEMENTATION, implementation.getClass()), e); + } + } + + @Override + public void destroy(Object instance) { + LOG.debug("{}: In destroy: instance: {}", logName(), instance); + + (( RoutedRpcRegistration)instance).close(); + } + + private String logName() { + return (container != null ? container.getBundleContext().getBundle().getSymbolicName() : "") + + " (" + id + ")"; + } +} diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcRegistrationConverter.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcRegistrationConverter.java new file mode 100644 index 0000000000..acc6a01d4a --- /dev/null +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RoutedRpcRegistrationConverter.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.blueprint.ext; + +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.osgi.service.blueprint.container.Converter; +import org.osgi.service.blueprint.container.ReifiedType; + +/** + * Implements a Converter that converts RoutedRpcRegistration instances. This is to work around an issue + * when injecting a RoutedRpcRegistration instance into a bean where Aries is not able to convert the instance + * returned from the RpcRegistryProvider to the desired generic RoutedRpcRegistration type specified in the + * bean's setter method. This is because the actual instance class specifies a generic type variable T and, + * even though it extends RpcService and should match, Aries doesn't handle it correctly. + * + * @author Thomas Pantelis + */ +public class RoutedRpcRegistrationConverter implements Converter { + @Override + public boolean canConvert(Object sourceObject, ReifiedType targetType) { + return sourceObject instanceof RoutedRpcRegistration && + RoutedRpcRegistration.class.isAssignableFrom(targetType.getRawClass()); + } + + @Override + public Object convert(Object sourceObject, ReifiedType targetType) throws Exception { + return sourceObject; + } +} diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcImplementationBean.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcImplementationBean.java new file mode 100644 index 0000000000..b81f833b12 --- /dev/null +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcImplementationBean.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.blueprint.ext; + +import com.google.common.base.Strings; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.osgi.framework.Bundle; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Blueprint bean corresponding to the "rpc-implementation" element that registers an RPC implementation with + * the RpcProviderRegistry. + * + * @author Thomas Pantelis + */ +public class RpcImplementationBean { + private static final Logger LOG = LoggerFactory.getLogger(RpcImplementationBean.class); + static final String RPC_IMPLEMENTATION = "rpc-implementation"; + + private RpcProviderRegistry rpcRegistry; + private Bundle bundle; + private String interfaceName; + private RpcService implementation; + private final List> rpcRegistrations = new ArrayList<>();; + + public void setRpcRegistry(RpcProviderRegistry rpcRegistry) { + this.rpcRegistry = rpcRegistry; + } + + public void setBundle(Bundle bundle) { + this.bundle = bundle; + } + + public void setInterfaceName(String interfaceName) { + this.interfaceName = interfaceName; + } + + public void setImplementation(RpcService implementation) { + this.implementation = implementation; + } + + public void init() { + try { + List> rpcInterfaces = getImplementedRpcServiceInterfaces(interfaceName, + implementation.getClass(), bundle, RPC_IMPLEMENTATION); + + LOG.debug("{}: init - adding implementation {} for RpcService interface(s) {}", bundle.getSymbolicName(), + implementation, rpcInterfaces); + + for(Class rpcInterface: rpcInterfaces) { + rpcRegistrations.add(rpcRegistry.addRpcImplementation(rpcInterface, implementation)); + } + } catch(ComponentDefinitionException e) { + throw e; + } catch(Exception e) { + throw new ComponentDefinitionException(String.format( + "Error processing \"%s\" for %s", RPC_IMPLEMENTATION, implementation.getClass()), e); + } + } + + public void destroy() { + for(RpcRegistration reg: rpcRegistrations) { + reg.close(); + } + } + + @SuppressWarnings("unchecked") + static List> getImplementedRpcServiceInterfaces(String interfaceName, + Class implementationClass, Bundle bundle, String logName) throws ClassNotFoundException { + if(!Strings.isNullOrEmpty(interfaceName)) { + Class rpcInterface = bundle.loadClass(interfaceName); + + if(!rpcInterface.isAssignableFrom(implementationClass)) { + throw new ComponentDefinitionException(String.format( + "The specified \"interface\" %s for \"%s\" is not implemented by RpcService \"ref\" %s", + interfaceName, logName, implementationClass)); + } + + return Collections.singletonList((Class)rpcInterface); + } + + List> rpcInterfaces = new ArrayList<>(); + for(Class intface: implementationClass.getInterfaces()) { + if(RpcService.class.isAssignableFrom(intface)) { + rpcInterfaces.add((Class) intface); + } + } + + if(rpcInterfaces.isEmpty()) { + throw new ComponentDefinitionException(String.format( + "The \"ref\" instance %s for \"%s\" does not implemented any RpcService interfaces", + implementationClass, logName)); + } + + return rpcInterfaces; + } +} diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcServiceMetadata.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcServiceMetadata.java new file mode 100644 index 0000000000..b48d04245b --- /dev/null +++ b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcServiceMetadata.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.blueprint.ext; + +import com.google.common.base.Preconditions; +import java.util.Collections; +import java.util.List; +import org.apache.aries.blueprint.ext.ComponentFactoryMetadata; +import org.apache.aries.blueprint.services.ExtendedBlueprintContainer; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory metadata corresponding to the "rpc-service" element that gets an RPC service implementation from + * the RpcProviderRegistry and provides it to the Blueprint container. + * + * @author Thomas Pantelis + */ +class RpcServiceMetadata implements ComponentFactoryMetadata { + private static final Logger LOG = LoggerFactory.getLogger(RpcServiceMetadata.class); + + private final String id; + private final String interfaceName; + private ExtendedBlueprintContainer container; + + RpcServiceMetadata(String id, String interfaceName) { + this.id = id; + this.interfaceName = interfaceName; + } + + @Override + public String getId() { + return id; + } + + @Override + public int getActivation() { + return ACTIVATION_LAZY; + } + + @Override + public List getDependsOn() { + return Collections.singletonList(OpendaylightNamespaceHandler.RPC_REGISTRY_NAME); + } + + @Override + public void init(ExtendedBlueprintContainer container) { + this.container = container; + + LOG.debug("{}: In init", logName()); + } + + @SuppressWarnings("unchecked") + @Override + public Object create() throws ComponentDefinitionException { + LOG.debug("{}: In create: interfaceName: {}", logName(), interfaceName); + + RpcProviderRegistry rpcRegistry = (RpcProviderRegistry) container.getComponentInstance( + OpendaylightNamespaceHandler.RPC_REGISTRY_NAME); + + try { + Class rpcInterface = container.getBundleContext().getBundle().loadClass(interfaceName); + Preconditions.checkArgument(RpcService.class.isAssignableFrom(rpcInterface), + "Specified interface %s is not an RpcService", interfaceName); + + RpcService rpcService = rpcRegistry.getRpcService((Class)rpcInterface); + + LOG.debug("{}: create returning service {}", logName(), rpcService); + + return rpcService; + } catch(Exception e) { + throw new ComponentDefinitionException("Error getting RPC service for " + interfaceName, e); + } + } + + @Override + public void destroy(Object instance) { + } + + private String logName() { + return (container != null ? container.getBundleContext().getBundle().getSymbolicName() : "") + + " (" + id + ")"; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RpcServiceMetadata [id=").append(id).append(", interfaceName=").append(interfaceName) + .append("]"); + return builder.toString(); + } +} 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 088e2fc5e3..3632ccb3a4 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 @@ -3,10 +3,29 @@ targetNamespace="http://opendaylight.org/xmlns/blueprint/v1.0.0" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0.0"> - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- 2.36.6