Add restart-dependents-on-update blueprint extension 76/36476/20
authorTom Pantelis <tpanteli@brocade.com>
Sat, 19 Mar 2016 20:51:29 +0000 (16:51 -0400)
committerAnil Vishnoi <vishnoianil@gmail.com>
Thu, 7 Apr 2016 18:09:45 +0000 (18:09 +0000)
Added an attribute extension to the <blueprint> element that adds a bean
processor that scans for any "property-placeholder" elements and reacts to
changes to the corresponding cfg file by restarting this blueprint
container and any dependent containers that consume OSGi services provided by
this container in an atomic and orderly manner. This mimics the module
restart functionality provided by the CSS.

A new OSGi service, BlueprintContainerRestartService, was added that
performs the orderly restart. This service is created and registered by
the BlueprintBundleTracker.

Change-Id: I5f463303c3a8aba35b3ed914268bdc67ac795a5a
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintBundleTracker.java
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintContainerRestartService.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintContainerRestartServiceImpl.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/ComponentProcessor.java [new file with mode: 0644]
opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/ext/OpendaylightNamespaceHandler.java [new file with mode: 0644]
opendaylight/blueprint/src/main/resources/opendaylight-blueprint-ext-1.0.0.xsd [new file with mode: 0644]

index 98e31e826b464741f5f0fe36a7f6a2c423245af3..5727c13ba1e888825d94b96bcc1c330bdf39fa1e 100644 (file)
@@ -12,7 +12,10 @@ import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import org.apache.aries.blueprint.NamespaceHandler;
 import org.apache.aries.blueprint.services.BlueprintExtenderService;
 import org.apache.aries.blueprint.services.BlueprintExtenderService;
+import org.apache.aries.util.AriesFrameworkUtil;
+import org.opendaylight.controller.blueprint.ext.OpendaylightNamespaceHandler;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -45,7 +48,9 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus
     private ServiceTracker<BlueprintExtenderService, BlueprintExtenderService> serviceTracker;
     private BundleTracker<Bundle> bundleTracker;
     private volatile BlueprintExtenderService blueprintExtenderService;
     private ServiceTracker<BlueprintExtenderService, BlueprintExtenderService> serviceTracker;
     private BundleTracker<Bundle> bundleTracker;
     private volatile BlueprintExtenderService blueprintExtenderService;
+    private volatile ServiceRegistration<?> blueprintContainerRestartReg;
     private ServiceRegistration<?> eventHandlerReg;
     private ServiceRegistration<?> eventHandlerReg;
+    private ServiceRegistration<?> namespaceReg;
 
     /**
      * Implemented from BundleActivator.
 
     /**
      * Implemented from BundleActivator.
@@ -54,11 +59,9 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus
     public void start(BundleContext context) {
         LOG.info("Starting {}", getClass().getSimpleName());
 
     public void start(BundleContext context) {
         LOG.info("Starting {}", getClass().getSimpleName());
 
-        // Register EventHandler for blueprint events
+        registerBlueprintEventHandler(context);
 
 
-        Dictionary<String, Object> props = new Hashtable<>();
-        props.put(org.osgi.service.event.EventConstants.EVENT_TOPIC, EventConstants.TOPIC_CREATED);
-        eventHandlerReg = context.registerService(EventHandler.class.getName(), this, props);
+        registerNamespaceHandler(context);
 
         bundleTracker = new BundleTracker<>(context, Bundle.ACTIVE, this);
 
 
         bundleTracker = new BundleTracker<>(context, Bundle.ACTIVE, this);
 
@@ -72,6 +75,10 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus
 
                         LOG.debug("Got BlueprintExtenderService");
 
 
                         LOG.debug("Got BlueprintExtenderService");
 
+                        blueprintContainerRestartReg = context.registerService(
+                                BlueprintContainerRestartService.class.getName(),
+                                new BlueprintContainerRestartServiceImpl(blueprintExtenderService), new Hashtable<>());
+
                         return blueprintExtenderService;
                     }
 
                         return blueprintExtenderService;
                     }
 
@@ -88,6 +95,19 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus
         serviceTracker.open();
     }
 
         serviceTracker.open();
     }
 
+    private void registerNamespaceHandler(BundleContext context) {
+        Dictionary<String, Object> props = new Hashtable<>();
+        props.put("osgi.service.blueprint.namespace", OpendaylightNamespaceHandler.NAMESPACE_1_0_0);
+        namespaceReg = context.registerService(NamespaceHandler.class.getName(),
+                new OpendaylightNamespaceHandler(), props);
+    }
+
+    private void registerBlueprintEventHandler(BundleContext context) {
+        Dictionary<String, Object> props = new Hashtable<>();
+        props.put(org.osgi.service.event.EventConstants.EVENT_TOPIC, EventConstants.TOPIC_CREATED);
+        eventHandlerReg = context.registerService(EventHandler.class.getName(), this, props);
+    }
+
     /**
      * Implemented from BundleActivator.
      */
     /**
      * Implemented from BundleActivator.
      */
@@ -95,7 +115,10 @@ public class BlueprintBundleTracker implements BundleActivator, BundleTrackerCus
     public void stop(BundleContext context) {
         bundleTracker.close();
         serviceTracker.close();
     public void stop(BundleContext context) {
         bundleTracker.close();
         serviceTracker.close();
-        eventHandlerReg.unregister();
+
+        AriesFrameworkUtil.safeUnregisterService(eventHandlerReg);
+        AriesFrameworkUtil.safeUnregisterService(namespaceReg);
+        AriesFrameworkUtil.safeUnregisterService(blueprintContainerRestartReg);
     }
 
     /**
     }
 
     /**
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintContainerRestartService.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintContainerRestartService.java
new file mode 100644 (file)
index 0000000..522415b
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Interface that restarts blueprint containers.
+ *
+ * @author Thomas Pantelis
+ */
+public interface BlueprintContainerRestartService {
+
+    /**
+     * Restarts the blueprint container for the given bundle and all its dependent containers in an atomic
+     * and orderly manner. The dependent containers are identified by walking the OSGi service dependency
+     * hierarchies for the service(s) provided by the given bundle.
+     *
+     * @param bundle the bundle to restart
+     */
+    void restartContainerAndDependents(Bundle bundle);
+}
\ No newline at end of file
diff --git a/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintContainerRestartServiceImpl.java b/opendaylight/blueprint/src/main/java/org/opendaylight/controller/blueprint/BlueprintContainerRestartServiceImpl.java
new file mode 100644 (file)
index 0000000..3e04f21
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.aries.blueprint.services.BlueprintExtenderService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Implementation of the BlueprintContainerRestartService.
+ *
+ * @author Thomas Pantelis
+ */
+class BlueprintContainerRestartServiceImpl implements AutoCloseable, BlueprintContainerRestartService {
+    private static final Logger LOG = LoggerFactory.getLogger(BlueprintContainerRestartServiceImpl.class);
+
+    private final ExecutorService restartExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().
+            setDaemon(true).setNameFormat("BlueprintContainerRestartService").build());
+    private final BlueprintExtenderService blueprintExtenderService;
+
+    BlueprintContainerRestartServiceImpl(BlueprintExtenderService blueprintExtenderService) {
+        this.blueprintExtenderService = blueprintExtenderService;
+    }
+
+    @Override
+    public void restartContainerAndDependents(final Bundle bundle) {
+        LOG.debug("restartContainerAndDependents for bundle {}", bundle);
+
+        restartExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                restartContainerAndDependentsInternal(bundle);
+
+            }
+        });
+    }
+
+    private void restartContainerAndDependentsInternal(Bundle forBundle) {
+        Set<Bundle> containerBundlesSet = new LinkedHashSet<>();
+        findDependentContainersRecursively(forBundle, containerBundlesSet);
+
+        List<Bundle> containerBundles = new ArrayList<>(containerBundlesSet);
+
+        LOG.info("Restarting blueprint containers for bundle {} and its dependent bundles {}", forBundle,
+                containerBundles.subList(1, containerBundles.size()));
+
+        // Destroy the containers in reverse order with 'forBundle' last, ie bottom-up in the service tree.
+        for(int i = containerBundles.size() - 1; i >= 0; i--) {
+            Bundle bundle = containerBundles.get(i);
+            blueprintExtenderService.destroyContainer(bundle, blueprintExtenderService.getContainer(bundle));
+        }
+
+        // Restart the containers top-down starting with 'forBundle'.
+        for(Bundle bundle: containerBundles) {
+            List<Object> paths = BlueprintBundleTracker.findBlueprintPaths(bundle);
+
+            LOG.info("Restarting blueprint container for bundle {} with paths {}", bundle, paths);
+
+            blueprintExtenderService.createContainer(bundle, paths);
+        }
+    }
+
+    /**
+     * Recursively finds the services registered by the given bundle and the bundles using those services.
+     * User bundles that have an associated blueprint container are added to containerBundles.
+     *
+     * @param bundle the bundle to traverse
+     * @param containerBundles the current set of bundles containing blueprint containers
+     */
+    private void findDependentContainersRecursively(Bundle bundle, Set<Bundle> containerBundles) {
+        if(containerBundles.contains(bundle)) {
+            return;
+        }
+
+        containerBundles.add(bundle);
+        ServiceReference<?>[] references = bundle.getRegisteredServices();
+        if (references != null) {
+            for (ServiceReference<?> reference : references) {
+                Bundle[] usingBundles = reference.getUsingBundles();
+                if(usingBundles != null) {
+                    for(Bundle usingBundle: usingBundles) {
+                        if(blueprintExtenderService.getContainer(usingBundle) != null) {
+                            findDependentContainersRecursively(usingBundle, containerBundles);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        restartExecutor.shutdownNow();
+    }
+}
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
new file mode 100644 (file)
index 0000000..ed87e70
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+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.util.AriesFrameworkUtil;
+import org.opendaylight.controller.blueprint.BlueprintContainerRestartService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.blueprint.reflect.BeanProperty;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+import org.osgi.service.blueprint.reflect.ValueMetadata;
+import org.osgi.service.cm.ManagedService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The singleton component processor that is invoked by the blueprint container to perform operations on
+ * various component definitions prior to component creation.
+ *
+ * @author Thomas Pantelis
+ */
+public class ComponentProcessor implements ComponentDefinitionRegistryProcessor {
+    private static final Logger LOG = LoggerFactory.getLogger(ComponentProcessor.class);
+    private static final String CM_PERSISTENT_ID_PROPERTY = "persistentId";
+
+    private final List<ServiceRegistration<?>> managedServiceRegs = new ArrayList<>();
+    private Bundle bundle;
+    private BlueprintContainerRestartService blueprintContainerRestartService;
+    private boolean restartDependentsOnUpdates;
+
+    public void setBundle(Bundle bundle) {
+        this.bundle = bundle;
+    }
+
+    public void setBlueprintContainerRestartService(BlueprintContainerRestartService restartService) {
+        this.blueprintContainerRestartService = restartService;
+    }
+
+    public void setRestartDependentsOnUpdates(boolean restartDependentsOnUpdates) {
+        this.restartDependentsOnUpdates = restartDependentsOnUpdates;
+    }
+
+    public void destroy() {
+        for(ServiceRegistration<?> reg: managedServiceRegs) {
+            AriesFrameworkUtil.safeUnregisterService(reg);
+        }
+    }
+
+    @Override
+    public void process(ComponentDefinitionRegistry registry) {
+        LOG.debug("{}: In process", bundle.getSymbolicName());
+
+        for(String name : registry.getComponentDefinitionNames()) {
+            ComponentMetadata component = registry.getComponentDefinition(name);
+            if(component instanceof MutableBeanMetadata) {
+                processMutableBeanMetadata((MutableBeanMetadata)component);
+            }
+        }
+    }
+
+    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(),
+                    bean.getRuntimeClass());
+
+            for(BeanProperty prop: bean.getProperties()) {
+                if(CM_PERSISTENT_ID_PROPERTY.equals(prop.getName())) {
+                    if(prop.getValue() instanceof ValueMetadata) {
+                        ValueMetadata persistentId = (ValueMetadata)prop.getValue();
+
+                        LOG.debug("{}: Found {} property, value : {}", bundle.getSymbolicName(),
+                                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());
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    private void registerManagedService(final String persistentId) {
+        // Register a ManagedService so we git updates from the ConfigAdmin when the cfg file corresponding
+        // to the persistentId changes.
+        ManagedService managedService = new ManagedService() {
+            private volatile boolean initialUpdate = true;
+
+            @Override
+            public void updated(Dictionary<String, ?> properties) {
+                LOG.debug("{}: ManagedService updated for persistentId {}, properties: {}, initialUpdate: {}",
+                        bundle.getSymbolicName(), 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
+                // yet exist.
+                if(initialUpdate) {
+                    initialUpdate = false;
+                } else {
+                    blueprintContainerRestartService.restartContainerAndDependents(bundle);
+                }
+            }
+        };
+
+        Dictionary<String, Object> props = new Hashtable<>();
+        props.put(Constants.SERVICE_PID, persistentId);
+        props.put(Constants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
+        props.put(Constants.BUNDLE_VERSION, bundle.getHeaders().get(Constants.BUNDLE_VERSION));
+        managedServiceRegs.add(bundle.getBundleContext().registerService(ManagedService.class.getName(),
+                managedService, props));
+    }
+}
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
new file mode 100644 (file)
index 0000000..89bea93
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.net.URL;
+import java.util.Collections;
+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.mutable.MutableBeanMetadata;
+import org.apache.aries.blueprint.mutable.MutableRefMetadata;
+import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
+import org.apache.aries.blueprint.mutable.MutableValueMetadata;
+import org.opendaylight.controller.blueprint.BlueprintContainerRestartService;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.reflect.BeanMetadata;
+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.ValueMetadata;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * The NamespaceHandler for Opendaylight blueprint extensions.
+ *
+ * @author Thomas Pantelis
+ */
+public class OpendaylightNamespaceHandler implements NamespaceHandler {
+    public static final String NAMESPACE_1_0_0 = "http://opendaylight.org/xmlns/blueprint/v1.0.0";
+
+    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";
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public Set<Class> getManagedClasses() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public URL getSchemaLocation(String namespace) {
+        if(NAMESPACE_1_0_0.equals(namespace)) {
+            URL url = getClass().getResource("/opendaylight-blueprint-ext-1.0.0.xsd");
+            LOG.debug("getSchemaLocation for {} returning URL {}", namespace, url);
+            return url;
+        }
+
+        return null;
+    }
+
+    @Override
+    public Metadata parse(Element element, ParserContext context) {
+        LOG.debug("In parse for {}", element);
+        throw new ComponentDefinitionException("Unsupported standalone element: " + element.getNodeName());
+    }
+
+    @Override
+    public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
+        if(node instanceof Attr) {
+            if (nodeNameEquals(node, RESTART_DEPENDENTS_ON_UPDATES)) {
+                return decorateRestartDependentsOnUpdates((Attr)node, component, context);
+            }
+
+            throw new ComponentDefinitionException("Unsupported attribute: " + node.getNodeName());
+        } else {
+            throw new ComponentDefinitionException("Unsupported node type: " + node);
+        }
+    }
+
+    private static ComponentMetadata decorateRestartDependentsOnUpdates(Attr attr, ComponentMetadata component,
+            ParserContext context) {
+        if(component != null) {
+            throw new ComponentDefinitionException("Attribute " + attr.getNodeName() +
+                    " can only be used on the root <blueprint> element");
+        }
+
+        LOG.debug("decorateRestartDependentsOnUpdates: {}", attr.getValue());
+
+        if(!Boolean.TRUE.equals(Boolean.valueOf(attr.getValue()))) {
+            return component;
+        }
+
+        MutableBeanMetadata metadata = registerComponentProcessor(context);
+        metadata.addProperty("restartDependentsOnUpdates", createValue(context, "true"));
+
+        return component;
+    }
+
+    private static MutableBeanMetadata registerComponentProcessor(ParserContext context) {
+        ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry();
+        MutableBeanMetadata metadata = (MutableBeanMetadata) registry.getComponentDefinition(COMPONENT_PROCESSOR_NAME);
+        if(metadata == null) {
+            metadata = context.createMetadata(MutableBeanMetadata.class);
+            metadata.setProcessor(true);
+            metadata.setId(COMPONENT_PROCESSOR_NAME);
+            metadata.setActivation(BeanMetadata.ACTIVATION_EAGER);
+            metadata.setScope(BeanMetadata.SCOPE_SINGLETON);
+            metadata.setRuntimeClass(ComponentProcessor.class);
+            metadata.setDestroyMethod("destroy");
+            metadata.addProperty("bundle", createRef(context, "blueprintBundle"));
+            metadata.addProperty("blueprintContainerRestartService", createServiceRef(context,
+                    BlueprintContainerRestartService.class, null));
+
+            LOG.debug("Registering ComponentProcessor bean: {}", metadata);
+
+            registry.registerComponentDefinition(metadata);
+        }
+
+        return metadata;
+    }
+
+    private static ValueMetadata createValue(ParserContext context, String value) {
+        MutableValueMetadata m = context.createMetadata(MutableValueMetadata.class);
+        m.setStringValue(value);
+        return m;
+    }
+
+    private static MutableReferenceMetadata createServiceRef(ParserContext context, Class<?> cls, String filter) {
+        MutableReferenceMetadata m = context.createMetadata(MutableReferenceMetadata.class);
+        m.setRuntimeInterface(cls);
+        m.setInterface(cls.getName());
+        m.setActivation(ReferenceMetadata.ACTIVATION_EAGER);
+        m.setAvailability(ReferenceMetadata.AVAILABILITY_MANDATORY);
+
+        if(filter != null) {
+            m.setFilter(filter);
+        }
+
+        return m;
+    }
+
+    private static RefMetadata createRef(ParserContext context, String value) {
+        MutableRefMetadata metadata = context.createMetadata(MutableRefMetadata.class);
+        metadata.setComponentId(value);
+        return metadata;
+    }
+
+    private static boolean nodeNameEquals(Node node, String name) {
+        return name.equals(node.getNodeName()) || name.equals(node.getLocalName());
+    }
+}
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..f83434f
--- /dev/null
@@ -0,0 +1,10 @@
+<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">
+
+<!-- <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/> -->
+
+  <xsd:attribute name="restart-dependents-on-updates" type="xsd:boolean"/>
+
+</xsd:schema>
\ No newline at end of file