Revert "Remove support for actions/rpc/notifications" 16/109816/1
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 17 Jan 2024 07:59:32 +0000 (08:59 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 17 Jan 2024 08:00:19 +0000 (09:00 +0100)
This reverts commit beaac808943b25f5627c70ddb5029408101204a7.

JIRA: CONTROLLER-2090
Change-Id: I3a624a4c5ef8159d2667794f734609f0a2be3e5b
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
13 files changed:
opendaylight/blueprint/pom.xml
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/AbstractInvokableServiceMetadata.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ActionProviderBean.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ActionServiceMetadata.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/NotificationListenerBean.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/OpendaylightNamespaceHandler.java
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcImplementationBean.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcServiceMetadata.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcUtil.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/SpecificReferenceListMetadata.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/StaticReferenceMetadata.java [new file with mode: 0644]
opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-1.0.0.xsd [new file with mode: 0644]
opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-2.0.0.xsd [deleted file]

index 42d660f98892361b1400ae0856a4b3e814444ec8..4eec604143edf35dd13c84c328e13f8f947ebd0f 100644 (file)
       <groupId>org.opendaylight.mdsal</groupId>
       <artifactId>mdsal-dom-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.mdsal</groupId>
+      <artifactId>mdsal-dom-spi</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.mdsal</groupId>
       <artifactId>mdsal-binding-api</artifactId>
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/AbstractInvokableServiceMetadata.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/AbstractInvokableServiceMetadata.java
new file mode 100644 (file)
index 0000000..1938fc3
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Set;
+import java.util.function.Predicate;
+import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
+import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
+import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.mdsal.dom.spi.ContentRoutedRpcContext;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+
+abstract class AbstractInvokableServiceMetadata extends AbstractDependentComponentFactoryMetadata {
+    private final String interfaceName;
+
+    private ListenerRegistration<DOMRpcAvailabilityListener> rpcListenerReg;
+    private RpcConsumerRegistry rpcRegistry;
+    private Class<RpcService> rpcInterface;
+    private Set<QName> rpcSchemaPaths;
+
+    AbstractInvokableServiceMetadata(final String id, final String interfaceName) {
+        super(id);
+        this.interfaceName = requireNonNull(interfaceName);
+    }
+
+    Class<RpcService> rpcInterface() {
+        return rpcInterface;
+    }
+
+    @SuppressWarnings({ "checkstyle:IllegalCatch", "unchecked" })
+    @Override
+    public final void init(final ExtendedBlueprintContainer container) {
+        super.init(container);
+
+        final Class<?> interfaceClass;
+        try {
+            interfaceClass = container().getBundleContext().getBundle().loadClass(interfaceName);
+        } catch (final Exception e) {
+            throw new ComponentDefinitionException(String.format("%s: Error obtaining interface class %s",
+                    logName(), interfaceName), e);
+        }
+
+        if (!RpcService.class.isAssignableFrom(interfaceClass)) {
+            throw new ComponentDefinitionException(String.format(
+                "%s: The specified interface %s is not an RpcService", logName(), interfaceName));
+        }
+
+        rpcInterface = (Class<RpcService>)interfaceClass;
+    }
+
+    @Override
+    protected final void startTracking() {
+        // Request RpcProviderRegistry first ...
+        retrieveService("RpcConsumerRegistry", RpcConsumerRegistry.class, this::onRpcRegistry);
+    }
+
+    private void onRpcRegistry(final Object service) {
+        log.debug("{}: Retrieved RpcProviderRegistry {}", logName(), service);
+        rpcRegistry = (RpcConsumerRegistry)service;
+
+        // Now acquire SchemaService...
+        retrieveService("SchemaService", DOMSchemaService.class, this::onSchemaService);
+    }
+
+    private void onSchemaService(final Object service) {
+        log.debug("{}: Retrieved SchemaService {}", logName(), service);
+
+        // Now get the SchemaContext and trigger RPC resolution
+        retrievedSchemaContext(((DOMSchemaService)service).getGlobalContext());
+    }
+
+    private void retrievedSchemaContext(final EffectiveModelContext schemaContext) {
+        log.debug("{}: retrievedSchemaContext", logName());
+
+        final Collection<QName> schemaPaths = RpcUtil.decomposeRpcService(rpcInterface, schemaContext,
+            rpcFilter());
+        if (schemaPaths.isEmpty()) {
+            log.debug("{}: interface {} has no acceptable entries, assuming it is satisfied", logName(), rpcInterface);
+            setSatisfied();
+            return;
+        }
+
+        rpcSchemaPaths = ImmutableSet.copyOf(schemaPaths);
+        log.debug("{}: Got SchemaPaths: {}", logName(), rpcSchemaPaths);
+
+        // First get the DOMRpcService OSGi service. This will be used to register a listener to be notified
+        // when the underlying DOM RPC service is available.
+        retrieveService("DOMRpcService", DOMRpcService.class, this::retrievedDOMRpcService);
+    }
+
+    private void retrievedDOMRpcService(final Object service) {
+        log.debug("{}: retrievedDOMRpcService {}", logName(), service);
+        final DOMRpcService domRpcService = (DOMRpcService)service;
+
+        setDependencyDesc("Available DOM RPC for binding RPC: " + rpcInterface);
+        rpcListenerReg = domRpcService.registerRpcListener(new DOMRpcAvailabilityListener() {
+            @Override
+            public void onRpcAvailable(final Collection<DOMRpcIdentifier> rpcs) {
+                onRpcsAvailable(rpcs);
+            }
+
+            @Override
+            public void onRpcUnavailable(final Collection<DOMRpcIdentifier> rpcs) {
+            }
+        });
+    }
+
+    abstract Predicate<ContentRoutedRpcContext> rpcFilter();
+
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    @Override
+    public final Object create() throws ComponentDefinitionException {
+        log.debug("{}: In create: interfaceName: {}", logName(), interfaceName);
+
+        super.onCreate();
+
+        try {
+            RpcService rpcService = rpcRegistry.getRpcService(rpcInterface);
+
+            log.debug("{}: create returning service {}", logName(), rpcService);
+
+            return rpcService;
+        } catch (final RuntimeException e) {
+            throw new ComponentDefinitionException("Error getting RPC service for " + interfaceName, e);
+        }
+    }
+
+    protected final void onRpcsAvailable(final Collection<DOMRpcIdentifier> rpcs) {
+        for (DOMRpcIdentifier identifier : rpcs) {
+            if (rpcSchemaPaths.contains(identifier.getType())) {
+                log.debug("{}: onRpcsAvailable - found SchemaPath {}", logName(), identifier.getType());
+                setSatisfied();
+                break;
+            }
+        }
+    }
+
+    @Override
+    public final void stopTracking() {
+        super.stopTracking();
+        closeRpcListenerReg();
+    }
+
+    private void closeRpcListenerReg() {
+        if (rpcListenerReg != null) {
+            rpcListenerReg.close();
+            rpcListenerReg = null;
+        }
+    }
+
+    @Override
+    public final void destroy(final Object instance) {
+        super.destroy(instance);
+        closeRpcListenerReg();
+    }
+
+    @Override
+    public final String toString() {
+        return MoreObjects.toStringHelper(this).add("id", getId()).add("interfaceName", interfaceName).toString();
+    }
+}
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ActionProviderBean.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ActionProviderBean.java
new file mode 100644 (file)
index 0000000..5139b5f
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 Cisco 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.collect.Collections2;
+import com.google.common.collect.ImmutableSet;
+import java.util.Objects;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+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 "action-provider" element that registers the promise to instantiate action
+ * instances with RpcProviderRegistry.
+ *
+ * <p>
+ * This bean has two distinct facets:
+ * - if a reference bean is provided, it registers it with {@link RpcProviderService}
+ * - if a reference bean is not provided, it registers the corresponding no-op implementation with
+ *   {@link DOMRpcProviderService} for all action (Routed RPC) elements in the provided interface
+ */
+public class ActionProviderBean {
+    static final String ACTION_PROVIDER = "action-provider";
+
+    private static final Logger LOG = LoggerFactory.getLogger(ActionProviderBean.class);
+
+    private DOMRpcProviderService domRpcProvider = null;
+    private RpcProviderService bindingRpcProvider = null;
+    private DOMSchemaService schemaService = null;
+    private RpcService implementation = null;
+    private String interfaceName = null;
+    private Registration reg;
+    private Bundle bundle = null;
+
+    public void setBundle(final Bundle bundle) {
+        this.bundle = bundle;
+    }
+
+    public void setInterfaceName(final String interfaceName) {
+        this.interfaceName = interfaceName;
+    }
+
+    public void setImplementation(final RpcService implementation) {
+        this.implementation = implementation;
+    }
+
+    public void setDomRpcProvider(final DOMRpcProviderService rpcProviderService) {
+        domRpcProvider = rpcProviderService;
+    }
+
+    public void setBindingRpcProvider(final RpcProviderService rpcProvider) {
+        bindingRpcProvider = rpcProvider;
+    }
+
+    public void setSchemaService(final DOMSchemaService schemaService) {
+        this.schemaService = schemaService;
+    }
+
+    public void init() {
+        // First resolve the interface class
+        final Class<RpcService> interfaceClass = getRpcClass();
+
+        LOG.debug("{}: resolved interface {} to {}", ACTION_PROVIDER, interfaceName, interfaceClass);
+
+        if (implementation != null) {
+            registerImplementation(interfaceClass);
+        } else {
+            registerFallback(interfaceClass);
+        }
+    }
+
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void destroy() {
+        if (reg != null) {
+            try {
+                reg.close();
+            } catch (final Exception e) {
+                LOG.warn("{}: error while unregistering", ACTION_PROVIDER, e);
+            } finally {
+                reg = null;
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<RpcService> getRpcClass() {
+        final Class<?> iface;
+
+        try {
+            iface = bundle.loadClass(interfaceName);
+        } catch (final ClassNotFoundException e) {
+            throw new ComponentDefinitionException(String.format(
+                "The specified \"interface\" for %s \"%s\" does not refer to an available class", interfaceName,
+                ACTION_PROVIDER), e);
+        }
+        if (!RpcService.class.isAssignableFrom(iface)) {
+            throw new ComponentDefinitionException(String.format(
+                "The specified \"interface\" %s for \"%s\" is not an RpcService", interfaceName, ACTION_PROVIDER));
+        }
+
+        return (Class<RpcService>) iface;
+    }
+
+    private void registerFallback(final Class<RpcService> interfaceClass) {
+        final var paths = RpcUtil.decomposeRpcService(interfaceClass, schemaService.getGlobalContext(),
+            Objects::nonNull);
+        if (paths.isEmpty()) {
+            LOG.warn("{}: interface {} has no actions defined", ACTION_PROVIDER, interfaceClass);
+            return;
+        }
+
+        final var rpcs = ImmutableSet.copyOf(Collections2.transform(paths, DOMRpcIdentifier::create));
+        reg = domRpcProvider.registerRpcImplementation(
+            (rpc, input) -> FluentFutures.immediateFailedFluentFuture(new DOMRpcImplementationNotAvailableException(
+                "Action %s has no instance matching %s", rpc, input)), rpcs);
+        LOG.debug("Registered provider for {}", interfaceName);
+    }
+
+    private void registerImplementation(final Class<RpcService> interfaceClass) {
+        if (!interfaceClass.isInstance(implementation)) {
+            throw new ComponentDefinitionException(String.format(
+                "The specified \"interface\" %s for \"%s\" is not implemented by RpcService \"ref\" %s",
+                interfaceName, ACTION_PROVIDER, implementation.getClass()));
+        }
+
+        reg = bindingRpcProvider.registerRpcImplementation(interfaceClass, implementation);
+        LOG.debug("Registered implementation {} for {}", implementation, interfaceName);
+    }
+}
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ActionServiceMetadata.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ActionServiceMetadata.java
new file mode 100644 (file)
index 0000000..7498eab
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Cisco 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.Objects;
+import java.util.function.Predicate;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
+import org.opendaylight.mdsal.dom.spi.ContentRoutedRpcContext;
+
+/**
+ * Factory metadata corresponding to the "action-service" element. It waits for a DOM promise of registration
+ * to appear in the {@link DOMRpcService} and then acquires a dynamic proxy via RpcProviderRegistry.
+ */
+final class ActionServiceMetadata extends AbstractInvokableServiceMetadata {
+    /*
+     * Implementation note:
+     *
+     * This implementation assumes Binding V1 semantics for actions, which means actions are packaged along with RPCs
+     * into a single interface. This has interesting implications on working with RpcServiceMetadata, which only
+     * handles the RPC side of the contract.
+     *
+     * Further interesting interactions stem from the fact that in DOM world each action is a separate entity, so the
+     * interface contract can let some actions to be invoked, while failing for others. This is a shortcoming of the
+     * Binding Specification and will be addressed in Binding V2 -- where each action is its own interface.
+     */
+    ActionServiceMetadata(final String id, final String interfaceName) {
+        super(id, interfaceName);
+    }
+
+    @Override
+    Predicate<ContentRoutedRpcContext> rpcFilter() {
+        return Objects::nonNull;
+    }
+}
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/NotificationListenerBean.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/NotificationListenerBean.java
new file mode 100644 (file)
index 0000000..55be59c
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.mdsal.binding.api.NotificationService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Blueprint bean corresponding to the "notification-listener" element that registers a NotificationListener
+ * with the NotificationService.
+ *
+ * @author Thomas Pantelis
+ */
+public class NotificationListenerBean {
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationListenerBean.class);
+    static final String NOTIFICATION_LISTENER = "notification-listener";
+
+    private Bundle bundle = null;
+    private NotificationService notificationService = null;
+    private NotificationListener notificationListener = null;
+    private ListenerRegistration<?> registration = null;
+
+    public void setNotificationService(final NotificationService notificationService) {
+        this.notificationService = notificationService;
+    }
+
+    public void setNotificationListener(final NotificationListener notificationListener) {
+        this.notificationListener = notificationListener;
+    }
+
+    public void setBundle(final Bundle bundle) {
+        this.bundle = bundle;
+    }
+
+    public void init() {
+        LOG.debug("{}: init - registering NotificationListener {}", bundle.getSymbolicName(), notificationListener);
+
+        registration = notificationService.registerNotificationListener(notificationListener);
+    }
+
+    public void destroy() {
+        if (registration != null) {
+            LOG.debug("{}: destroy - closing ListenerRegistration {}", bundle.getSymbolicName(), notificationListener);
+            registration.close();
+        } else {
+            LOG.debug("{}: destroy - listener was not registered", bundle.getSymbolicName());
+        }
+    }
+}
index b766e59cdcad2ecf03d10bcefc35acd096798e9e..af26acae0a16e1d7fd1a40d67181b8f2f682a51b 100644 (file)
@@ -16,6 +16,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;
@@ -23,6 +24,10 @@ 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.mdsal.binding.api.NotificationService;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
+import org.opendaylight.mdsal.dom.api.DOMSchemaService;
 import org.opendaylight.yangtools.util.xml.UntrustedXML;
 import org.osgi.service.blueprint.container.ComponentDefinitionException;
 import org.osgi.service.blueprint.reflect.BeanMetadata;
@@ -64,7 +69,12 @@ public final class OpendaylightNamespaceHandler implements NamespaceHandler {
     private static final String USE_DEFAULT_FOR_REFERENCE_TYPES = "use-default-for-reference-types";
     private static final String CLUSTERED_APP_CONFIG = "clustered-app-config";
     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";
+    private static final String ACTION_SERVICE = "action-service";
+    private static final String SPECIFIC_SERVICE_REF_LIST = "specific-reference-list";
+    private static final String STATIC_REFERENCE = "static-reference";
 
     @SuppressWarnings("rawtypes")
     @Override
@@ -87,8 +97,22 @@ public final class OpendaylightNamespaceHandler implements NamespaceHandler {
     public Metadata parse(final Element element, final ParserContext context) {
         LOG.debug("In parse for {}", element);
 
-        if (nodeNameEquals(element, CLUSTERED_APP_CONFIG)) {
+        if (nodeNameEquals(element, RpcImplementationBean.RPC_IMPLEMENTATION)) {
+            return parseRpcImplementation(element, context);
+        } else if (nodeNameEquals(element, RPC_SERVICE)) {
+            return parseRpcService(element, context);
+        } else if (nodeNameEquals(element, NotificationListenerBean.NOTIFICATION_LISTENER)) {
+            return parseNotificationListener(element, context);
+        } else if (nodeNameEquals(element, CLUSTERED_APP_CONFIG)) {
             return parseClusteredAppConfig(element, context);
+        } else if (nodeNameEquals(element, SPECIFIC_SERVICE_REF_LIST)) {
+            return parseSpecificReferenceList(element, context);
+        } else if (nodeNameEquals(element, STATIC_REFERENCE)) {
+            return parseStaticReference(element, context);
+        } else if (nodeNameEquals(element, ACTION_SERVICE)) {
+            return parseActionService(element, context);
+        } else if (nodeNameEquals(element, ActionProviderBean.ACTION_PROVIDER)) {
+            return parseActionProvider(element, context);
         }
 
         throw new ComponentDefinitionException("Unsupported standalone element: " + element.getNodeName());
@@ -120,10 +144,12 @@ public final class OpendaylightNamespaceHandler implements NamespaceHandler {
 
     private static ComponentMetadata decorateServiceType(final Attr attr, final ComponentMetadata component,
             final ParserContext context) {
-        if (!(component instanceof MutableServiceMetadata service)) {
+        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()));
@@ -209,6 +235,107 @@ public final class OpendaylightNamespaceHandler implements NamespaceHandler {
         return metadata;
     }
 
+    private static Metadata parseActionProvider(final Element element, final ParserContext context) {
+        registerDomRpcProviderServiceRefBean(context);
+        registerBindingRpcProviderServiceRefBean(context);
+        registerSchemaServiceRefBean(context);
+
+        MutableBeanMetadata metadata = createBeanMetadata(context, context.generateId(), ActionProviderBean.class,
+                true, true);
+        addBlueprintBundleRefProperty(context, metadata);
+        metadata.addProperty("domRpcProvider", createRef(context, DOM_RPC_PROVIDER_SERVICE_NAME));
+        metadata.addProperty("bindingRpcProvider", createRef(context, BINDING_RPC_PROVIDER_SERVICE_NAME));
+        metadata.addProperty("schemaService", createRef(context, SCHEMA_SERVICE_NAME));
+        metadata.addProperty("interfaceName", createValue(context, element.getAttribute(INTERFACE)));
+
+        if (element.hasAttribute(REF_ATTR)) {
+            metadata.addProperty("implementation", createRef(context, element.getAttribute(REF_ATTR)));
+        }
+
+        LOG.debug("parseActionProvider returning {}", metadata);
+        return metadata;
+    }
+
+
+    private static Metadata parseRpcImplementation(final Element element, final ParserContext context) {
+        registerBindingRpcProviderServiceRefBean(context);
+
+        MutableBeanMetadata metadata = createBeanMetadata(context, context.generateId(), RpcImplementationBean.class,
+                true, true);
+        addBlueprintBundleRefProperty(context, metadata);
+        metadata.addProperty("rpcProvider", createRef(context, BINDING_RPC_PROVIDER_SERVICE_NAME));
+        metadata.addProperty("implementation", createRef(context, element.getAttribute(REF_ATTR)));
+
+        if (element.hasAttribute(INTERFACE)) {
+            metadata.addProperty("interfaceName", createValue(context, element.getAttribute(INTERFACE)));
+        }
+
+        LOG.debug("parseRpcImplementation returning {}", metadata);
+        return metadata;
+    }
+
+    private static Metadata parseActionService(final Element element, final ParserContext context) {
+        ComponentFactoryMetadata metadata = new ActionServiceMetadata(getId(context, element),
+                element.getAttribute(INTERFACE));
+
+        LOG.debug("parseActionService returning {}", metadata);
+
+        return metadata;
+    }
+
+    private static Metadata parseRpcService(final Element element, final ParserContext context) {
+        ComponentFactoryMetadata metadata = new RpcServiceMetadata(getId(context, element),
+                element.getAttribute(INTERFACE));
+
+        LOG.debug("parseRpcService returning {}", metadata);
+
+        return metadata;
+    }
+
+    private static void registerDomRpcProviderServiceRefBean(final ParserContext context) {
+        registerRefBean(context, DOM_RPC_PROVIDER_SERVICE_NAME, DOMRpcProviderService.class);
+    }
+
+    private static void registerBindingRpcProviderServiceRefBean(final ParserContext context) {
+        registerRefBean(context, BINDING_RPC_PROVIDER_SERVICE_NAME, RpcProviderService.class);
+    }
+
+    private static void registerSchemaServiceRefBean(final ParserContext context) {
+        registerRefBean(context, SCHEMA_SERVICE_NAME, DOMSchemaService.class);
+    }
+
+    private static void registerRefBean(final ParserContext context, final String name, final Class<?> clazz) {
+        ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry();
+        if (registry.getComponentDefinition(name) == null) {
+            MutableReferenceMetadata metadata = createServiceRef(context, clazz, null);
+            metadata.setId(name);
+            registry.registerComponentDefinition(metadata);
+        }
+    }
+
+    private static Metadata parseNotificationListener(final Element element, final ParserContext context) {
+        registerNotificationServiceRefBean(context);
+
+        MutableBeanMetadata metadata = createBeanMetadata(context, context.generateId(), NotificationListenerBean.class,
+                true, true);
+        addBlueprintBundleRefProperty(context, metadata);
+        metadata.addProperty("notificationService", createRef(context, NOTIFICATION_SERVICE_NAME));
+        metadata.addProperty("notificationListener", createRef(context, element.getAttribute(REF_ATTR)));
+
+        LOG.debug("parseNotificationListener returning {}", metadata);
+
+        return metadata;
+    }
+
+    private static void registerNotificationServiceRefBean(final ParserContext context) {
+        ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry();
+        if (registry.getComponentDefinition(NOTIFICATION_SERVICE_NAME) == null) {
+            MutableReferenceMetadata metadata = createServiceRef(context, NotificationService.class, null);
+            metadata.setId(NOTIFICATION_SERVICE_NAME);
+            registry.registerComponentDefinition(metadata);
+        }
+    }
+
     private static Metadata parseClusteredAppConfig(final Element element, final ParserContext context) {
         LOG.debug("parseClusteredAppConfig");
 
@@ -256,6 +383,24 @@ public final class OpendaylightNamespaceHandler implements NamespaceHandler {
         }
     }
 
+    private static Metadata parseSpecificReferenceList(final Element element, final ParserContext context) {
+        ComponentFactoryMetadata metadata = new SpecificReferenceListMetadata(getId(context, element),
+                element.getAttribute(INTERFACE));
+
+        LOG.debug("parseSpecificReferenceList returning {}", metadata);
+
+        return metadata;
+    }
+
+    private static Metadata parseStaticReference(final Element element, final ParserContext context) {
+        ComponentFactoryMetadata metadata = new StaticReferenceMetadata(getId(context, element),
+                element.getAttribute(INTERFACE));
+
+        LOG.debug("parseStaticReference returning {}", metadata);
+
+        return metadata;
+    }
+
     private static Element parseXML(final String name, final String xml) {
         try {
             return UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(xml))).getDocumentElement();
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 (file)
index 0000000..a8fc47a
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+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 final List<ObjectRegistration<RpcService>> rpcRegistrations = new ArrayList<>();
+    private RpcProviderService rpcProvider = null;
+    private Bundle bundle = null;
+    private String interfaceName = null;
+    private RpcService implementation = null;
+
+    public void setRpcProvider(final RpcProviderService rpcProvider) {
+        this.rpcProvider = rpcProvider;
+    }
+
+    public void setBundle(final Bundle bundle) {
+        this.bundle = bundle;
+    }
+
+    public void setInterfaceName(final String interfaceName) {
+        this.interfaceName = interfaceName;
+    }
+
+    public void setImplementation(final RpcService implementation) {
+        this.implementation = implementation;
+    }
+
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void init() {
+        try {
+            final var rpcInterfaces = getImplementedRpcServiceInterfaces(interfaceName,
+                    implementation.getClass(), bundle, RPC_IMPLEMENTATION);
+
+            LOG.debug("{}: init - adding implementation {} for RpcService interface(s) {}", bundle.getSymbolicName(),
+                    implementation, rpcInterfaces);
+
+            for (var rpcInterface : rpcInterfaces) {
+                rpcRegistrations.add(rpcProvider.registerRpcImplementation(rpcInterface, implementation));
+            }
+        } catch (final ComponentDefinitionException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new ComponentDefinitionException(String.format(
+                    "Error processing \"%s\" for %s", RPC_IMPLEMENTATION, implementation.getClass()), e);
+        }
+    }
+
+    public void destroy() {
+        for (ObjectRegistration<RpcService> reg: rpcRegistrations) {
+            reg.close();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    static List<Class<RpcService>> getImplementedRpcServiceInterfaces(final String interfaceName,
+            final Class<?> implementationClass, final Bundle bundle, final 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<RpcService>)rpcInterface);
+        }
+
+        final var rpcInterfaces = new ArrayList<Class<RpcService>>();
+        for (var intface : implementationClass.getInterfaces()) {
+            if (RpcService.class.isAssignableFrom(intface)) {
+                rpcInterfaces.add((Class<RpcService>) 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 (file)
index 0000000..28590c1
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.Objects;
+import java.util.function.Predicate;
+import org.opendaylight.mdsal.dom.spi.ContentRoutedRpcContext;
+
+/**
+ * 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
+ */
+final class RpcServiceMetadata extends AbstractInvokableServiceMetadata {
+    RpcServiceMetadata(final String id, final String interfaceName) {
+        super(id, interfaceName);
+    }
+
+    @Override
+    Predicate<ContentRoutedRpcContext> rpcFilter() {
+        return Objects::isNull;
+    }
+}
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcUtil.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/RpcUtil.java
new file mode 100644 (file)
index 0000000..0d17e38
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 Cisco 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.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.dom.spi.ContentRoutedRpcContext;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for dealing with various aspects of RPCs and actions.
+ */
+final class RpcUtil {
+    private static final Logger LOG = LoggerFactory.getLogger(RpcUtil.class);
+
+    private RpcUtil() {
+        // Hidden on purpose
+    }
+
+    static List<QName> decomposeRpcService(final Class<RpcService> service,
+            final EffectiveModelContext schemaContext, final Predicate<ContentRoutedRpcContext> filter) {
+        final var moduleName = BindingReflections.getQNameModule(service);
+        final var module = schemaContext.findModuleStatement(moduleName)
+            .orElseThrow(() -> new IllegalArgumentException("Module not found in SchemaContext: " + moduleName
+                + "; service: " + service));
+        LOG.debug("Resolved service {} to module {}", service, module);
+
+        return module.streamEffectiveSubstatements(RpcEffectiveStatement.class)
+            .filter(rpc -> filter.test(ContentRoutedRpcContext.forRpc(rpc)))
+            .map(RpcEffectiveStatement::argument)
+            .collect(Collectors.toList());
+    }
+}
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/SpecificReferenceListMetadata.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/SpecificReferenceListMetadata.java
new file mode 100644 (file)
index 0000000..a98a062
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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.io.Resources;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Factory metadata corresponding to the "specific-reference-list" element that obtains a specific list
+ * of service instances from the OSGi registry for a given interface. The specific list is learned by first
+ * extracting the list of expected service types by inspecting RESOLVED bundles for a resource file under
+ * META-INF/services with the same name as the given interface. The type(s) listed in the resource file
+ * must match the "type" property of the advertised service(s). In this manner, an app bundle announces the
+ * service type(s) that it will advertise so that this class knows which services to expect up front. Once
+ * all the expected services are obtained, the container is notified that all dependencies of this component
+ * factory are satisfied.
+ *
+ * @author Thomas Pantelis
+ */
+class SpecificReferenceListMetadata extends AbstractDependentComponentFactoryMetadata {
+    private static final Logger LOG = LoggerFactory.getLogger(SpecificReferenceListMetadata.class);
+
+    private final String interfaceName;
+    private final String serviceResourcePath;
+    private final Collection<String> expectedServiceTypes = new ConcurrentSkipListSet<>();
+    private final Collection<String> retrievedServiceTypes = new ConcurrentSkipListSet<>();
+    private final Collection<Object> retrievedServices = Collections.synchronizedList(new ArrayList<>());
+    private volatile BundleTracker<Bundle> bundleTracker;
+    private volatile ServiceTracker<Object, Object> serviceTracker;
+
+    SpecificReferenceListMetadata(final String id, final String interfaceName) {
+        super(id);
+        this.interfaceName = interfaceName;
+        serviceResourcePath = "META-INF/services/" + interfaceName;
+    }
+
+    @Override
+    protected void startTracking() {
+        BundleTrackerCustomizer<Bundle> bundleListener = new BundleTrackerCustomizer<>() {
+            @Override
+            public Bundle addingBundle(final Bundle bundle, final BundleEvent event) {
+                bundleAdded(bundle);
+                return bundle;
+            }
+
+            @Override
+            public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Bundle object) {
+            }
+
+            @Override
+            public void removedBundle(final Bundle bundle, final BundleEvent event, final Bundle object) {
+            }
+        };
+
+        bundleTracker = new BundleTracker<>(container().getBundleContext(), Bundle.RESOLVED | Bundle.STARTING
+                | Bundle.STOPPING | Bundle.ACTIVE, bundleListener);
+
+        // This will get the list of all current RESOLVED+ bundles.
+        bundleTracker.open();
+
+        if (expectedServiceTypes.isEmpty()) {
+            setSatisfied();
+            return;
+        }
+
+        ServiceTrackerCustomizer<Object, Object> serviceListener = new ServiceTrackerCustomizer<>() {
+            @Override
+            public Object addingService(final ServiceReference<Object> reference) {
+                return serviceAdded(reference);
+            }
+
+            @Override
+            public void modifiedService(final ServiceReference<Object> reference, final Object service) {
+            }
+
+            @Override
+            public void removedService(final ServiceReference<Object> reference, final Object service) {
+                container().getBundleContext().ungetService(reference);
+            }
+        };
+
+        setDependencyDesc(interfaceName + " services with types " + expectedServiceTypes);
+
+        serviceTracker = new ServiceTracker<>(container().getBundleContext(), interfaceName, serviceListener);
+        serviceTracker.open();
+    }
+
+    private void bundleAdded(final Bundle bundle) {
+        URL resource = bundle.getEntry(serviceResourcePath);
+        if (resource == null) {
+            return;
+        }
+
+        LOG.debug("{}: Found {} resource in bundle {}", logName(), resource, bundle.getSymbolicName());
+
+        try {
+            for (String line : Resources.readLines(resource, StandardCharsets.UTF_8)) {
+                int ci = line.indexOf('#');
+                if (ci >= 0) {
+                    line = line.substring(0, ci);
+                }
+
+                line = line.trim();
+                if (line.isEmpty()) {
+                    continue;
+                }
+
+                String serviceType = line;
+                LOG.debug("{}: Retrieved service type {}", logName(), serviceType);
+                expectedServiceTypes.add(serviceType);
+            }
+        } catch (final IOException e) {
+            setFailure(String.format("%s: Error reading resource %s from bundle %s", logName(), resource,
+                    bundle.getSymbolicName()), e);
+        }
+    }
+
+    private Object serviceAdded(final ServiceReference<Object> reference) {
+        Object service = container().getBundleContext().getService(reference);
+        String serviceType = (String) reference.getProperty(OpendaylightNamespaceHandler.TYPE_ATTR);
+
+        LOG.debug("{}: Service type {} added from bundle {}", logName(), serviceType,
+                reference.getBundle().getSymbolicName());
+
+        if (serviceType == null) {
+            LOG.error("{}: Missing OSGi service property '{}' for service interface {} in bundle {}", logName(),
+                    OpendaylightNamespaceHandler.TYPE_ATTR, interfaceName,  reference.getBundle().getSymbolicName());
+            return service;
+        }
+
+        if (!expectedServiceTypes.contains(serviceType)) {
+            LOG.error("{}: OSGi service property '{}' for service interface {} in bundle {} was not found in the "
+                    + "expected service types {} obtained via {} bundle resources. Is the bundle resource missing or "
+                    + "the service type misspelled?", logName(), OpendaylightNamespaceHandler.TYPE_ATTR, interfaceName,
+                    reference.getBundle().getSymbolicName(), expectedServiceTypes, serviceResourcePath);
+            return service;
+        }
+
+        // If already satisfied, meaning we got all initial services, then a new bundle must've been
+        // dynamically installed or a prior service's blueprint container was restarted, in which case we
+        // restart our container.
+        if (isSatisfied()) {
+            restartContainer();
+        } else {
+            retrievedServiceTypes.add(serviceType);
+            retrievedServices.add(service);
+
+            if (retrievedServiceTypes.equals(expectedServiceTypes)) {
+                LOG.debug("{}: Got all expected service types", logName());
+                setSatisfied();
+            } else {
+                Set<String> remaining = new HashSet<>(expectedServiceTypes);
+                remaining.removeAll(retrievedServiceTypes);
+                setDependencyDesc(interfaceName + " services with types " + remaining);
+            }
+        }
+
+        return service;
+    }
+
+    @Override
+    public Object create() throws ComponentDefinitionException {
+        LOG.debug("{}: In create: interfaceName: {}", logName(), interfaceName);
+
+        super.onCreate();
+
+        LOG.debug("{}: create returning service list {}", logName(), retrievedServices);
+
+        synchronized (retrievedServices) {
+            return List.copyOf(retrievedServices);
+        }
+    }
+
+    @Override
+    public void destroy(final Object instance) {
+        super.destroy(instance);
+
+        if (bundleTracker != null) {
+            bundleTracker.close();
+            bundleTracker = null;
+        }
+
+        if (serviceTracker != null) {
+            serviceTracker.close();
+            serviceTracker = null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+            .append("SpecificReferenceListMetadata [interfaceName=").append(interfaceName)
+            .append(", serviceResourcePath=").append(serviceResourcePath).append("]")
+            .toString();
+    }
+}
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/StaticReferenceMetadata.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/StaticReferenceMetadata.java
new file mode 100644 (file)
index 0000000..97c04af
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Factory metadata corresponding to the "static-reference" element that obtains an OSGi service and
+ * returns the actual instance. This differs from the standard "reference" element that returns a dynamic
+ * proxy whose underlying service instance can come and go.
+ *
+ * @author Thomas Pantelis
+ */
+class StaticReferenceMetadata extends AbstractDependentComponentFactoryMetadata {
+    private static final Logger LOG = LoggerFactory.getLogger(StaticReferenceMetadata.class);
+
+    private final String interfaceName;
+    private volatile Object retrievedService;
+
+    StaticReferenceMetadata(final String id, final String interfaceName) {
+        super(id);
+        this.interfaceName = interfaceName;
+    }
+
+    @Override
+    protected void startTracking() {
+        retrieveService(interfaceName, interfaceName, service -> {
+            retrievedService = service;
+            setSatisfied();
+        });
+    }
+
+    @Override
+    public Object create() throws ComponentDefinitionException {
+        super.onCreate();
+
+        LOG.debug("{}: create returning service {}", logName(), retrievedService);
+
+        return retrievedService;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("StaticReferenceMetadata [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
new file mode 100644 (file)
index 0000000..297bbb4
--- /dev/null
@@ -0,0 +1,117 @@
+<xsd:schema xmlns="http://opendaylight.org/xmlns/blueprint/v1.0.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+    targetNamespace="http://opendaylight.org/xmlns/blueprint/v1.0.0" elementFormDefault="qualified"
+    attributeFormDefault="unqualified" version="1.0.0">
+
+  <!--
+   Copyright © 2016, 2018 Brocade Communications Systems, Inc. and others.
+
+   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
+   -->
+
+  <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>
+
+  <xsd:attribute name="restart-dependents-on-updates" type="xsd:boolean"/>
+  <xsd:attribute name="use-default-for-reference-types" type="xsd:boolean"/>
+  <xsd:attribute name="type" type="xsd:string"/>
+
+  <!--
+       String representation of an instance identifier. Precise string format
+       is undefined just now.
+   -->
+  <xsd:simpleType name="Tpath">
+    <xsd:restriction base="xsd:string"/>
+  </xsd:simpleType>
+
+  <!--
+       A promise to instantiate actions of specified binding interface.
+       Specified interface must define at least one action. In case a bean
+       is specified, it will be used as the implementation of last resort
+       for actions not otherwise bound. If a bean is not specified, a blank
+       implementation will be substituted.
+   -->
+  <xsd:complexType name="TactionProvider">
+    <xsd:attribute name="interface" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="ref" type="bp:Tidref" use="optional"/>
+  </xsd:complexType>
+  <xsd:element name="action-provider" type="TactionProvider"/>
+
+  <!--
+       A reference to an action-provider providing specified interface.
+       Specified interface must define at least one action.
+   -->
+  <xsd:complexType name="TactionService">
+    <xsd:attribute name="interface" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+  <xsd:element name="action-service" type="TactionService"/>
+
+  <!--
+       Implementation of an action at specific instance. This is used
+       to provide binding to actions on specific context, rather than
+       to a more general contract of action-provider.
+  <xsd:complexType name="TactionImplementation">
+    <xsd:attribute name="interface" type="bp:Tclass" use="optional"/>
+    <xsd:attribute name="ref" type="bp:Tidref" use="required"/>
+    <xsd:attribute name="path" type="Tpath" use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+  <xsd:element name="action-implementation" type="TactionImplementation"/ -->
+
+  <!--
+       A reference to a specific action-implementation.
+  <xsd:complexType name="TactionInstance">
+    <xsd:attribute name="interface" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="path" type="Tpath" use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+  <xsd:element name="action-instance" type="TactionInstance"/ -->
+
+  <xsd:complexType name="TrpcImplementation">
+    <xsd:attribute name="interface" type="bp:Tclass" use="optional"/>
+    <xsd:attribute name="ref" type="bp:Tidref" use="required"/>
+  </xsd:complexType>
+  <xsd:element name="rpc-implementation" type="TrpcImplementation"/>
+
+  <xsd:complexType name="TrpcService">
+    <xsd:attribute name="interface" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+  <xsd:element name="rpc-service" type="TrpcService"/>
+
+  <xsd:complexType name="TnotificationListener">
+    <xsd:attribute name="ref" type="bp:Tidref" use="required"/>
+  </xsd:complexType>
+  <xsd:element name="notification-listener" type="TnotificationListener"/>
+
+  <xsd:complexType name="TclusteredAppConfig">
+    <xsd:sequence>
+      <xsd:element name="default-config" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+    </xsd:sequence>
+    <xsd:attribute name="binding-class" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="list-key-value" type="xsd:string" use="optional"/>
+    <xsd:attribute name="default-config-file-name" type="xsd:string" use="optional"/>
+    <xsd:attribute name="id" type="xsd:ID" use="required"/>
+    <xsd:attribute name="update-strategy" type="TupdateStrategy" use="optional" default="reload"/>
+  </xsd:complexType>
+  <xsd:element name="clustered-app-config" type="TclusteredAppConfig"/>
+ <xsd:simpleType name="TupdateStrategy">
+  <xsd:restriction base="xsd:NMTOKEN">
+    <xsd:enumeration value="none"/>
+    <xsd:enumeration value="reload"/>
+  </xsd:restriction>
+ </xsd:simpleType>
+  <xsd:complexType name="TspecificReferenceList">
+    <xsd:attribute name="interface" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+  <xsd:element name="specific-reference-list" type="TspecificReferenceList"/>
+
+  <xsd:complexType name="TstaticReference">
+    <xsd:attribute name="interface" type="bp:Tclass" use="required"/>
+    <xsd:attribute name="id" type="xsd:ID"/>
+  </xsd:complexType>
+  <xsd:element name="static-reference" type="TstaticReference"/>
+</xsd:schema>
diff --git a/opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-2.0.0.xsd b/opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-2.0.0.xsd
deleted file mode 100644 (file)
index 1a1683e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<xsd:schema xmlns="http://opendaylight.org/xmlns/blueprint/v2.0.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
-    xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-    targetNamespace="http://opendaylight.org/xmlns/blueprint/v2.0.0" elementFormDefault="qualified"
-    attributeFormDefault="unqualified" version="2.0.0">
-
-  <!--
-   Copyright © 2016, 2018 Brocade Communications Systems, Inc. and others.
-
-   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
-   -->
-
-  <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>
-
-  <xsd:attribute name="restart-dependents-on-updates" type="xsd:boolean"/>
-  <xsd:attribute name="use-default-for-reference-types" type="xsd:boolean"/>
-  <xsd:attribute name="type" type="xsd:string"/>
-
-  <xsd:complexType name="TclusteredAppConfig">
-    <xsd:sequence>
-      <xsd:element name="default-config" type="xsd:string" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="binding-class" type="bp:Tclass" use="required"/>
-    <xsd:attribute name="list-key-value" type="xsd:string" use="optional"/>
-    <xsd:attribute name="default-config-file-name" type="xsd:string" use="optional"/>
-    <xsd:attribute name="id" type="xsd:ID" use="required"/>
-    <xsd:attribute name="update-strategy" type="TupdateStrategy" use="optional" default="reload"/>
-  </xsd:complexType>
-  <xsd:element name="clustered-app-config" type="TclusteredAppConfig"/>
-  <xsd:simpleType name="TupdateStrategy">
-    <xsd:restriction base="xsd:NMTOKEN">
-      <xsd:enumeration value="none"/>
-      <xsd:enumeration value="reload"/>
-    </xsd:restriction>
-  </xsd:simpleType>
-</xsd:schema>