Remove deprecated MD-SAL APIs
[controller.git] / opendaylight / blueprint / src / main / java / org / opendaylight / controller / blueprint / ext / OpendaylightNamespaceHandler.java
1 /*
2  * Copyright (c) 2016 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.blueprint.ext;
9
10 import com.google.common.base.Strings;
11 import java.io.IOException;
12 import java.io.StringReader;
13 import java.net.URL;
14 import java.util.Collections;
15 import java.util.Set;
16 import org.apache.aries.blueprint.ComponentDefinitionRegistry;
17 import org.apache.aries.blueprint.NamespaceHandler;
18 import org.apache.aries.blueprint.ParserContext;
19 import org.apache.aries.blueprint.ext.ComponentFactoryMetadata;
20 import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
21 import org.apache.aries.blueprint.mutable.MutableRefMetadata;
22 import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
23 import org.apache.aries.blueprint.mutable.MutableServiceMetadata;
24 import org.apache.aries.blueprint.mutable.MutableServiceReferenceMetadata;
25 import org.apache.aries.blueprint.mutable.MutableValueMetadata;
26 import org.opendaylight.controller.blueprint.BlueprintContainerRestartService;
27 import org.opendaylight.mdsal.binding.api.NotificationService;
28 import org.opendaylight.mdsal.binding.api.RpcProviderService;
29 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
30 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
31 import org.opendaylight.yangtools.util.xml.UntrustedXML;
32 import org.osgi.service.blueprint.container.ComponentDefinitionException;
33 import org.osgi.service.blueprint.reflect.BeanMetadata;
34 import org.osgi.service.blueprint.reflect.ComponentMetadata;
35 import org.osgi.service.blueprint.reflect.Metadata;
36 import org.osgi.service.blueprint.reflect.RefMetadata;
37 import org.osgi.service.blueprint.reflect.ReferenceMetadata;
38 import org.osgi.service.blueprint.reflect.ServiceMetadata;
39 import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata;
40 import org.osgi.service.blueprint.reflect.ValueMetadata;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.w3c.dom.Attr;
44 import org.w3c.dom.Element;
45 import org.w3c.dom.Node;
46 import org.w3c.dom.NodeList;
47 import org.xml.sax.InputSource;
48 import org.xml.sax.SAXException;
49
50 /**
51  * The NamespaceHandler for Opendaylight blueprint extensions.
52  *
53  * @author Thomas Pantelis
54  */
55 public final class OpendaylightNamespaceHandler implements NamespaceHandler {
56     public static final String NAMESPACE_1_0_0 = "http://opendaylight.org/xmlns/blueprint/v1.0.0";
57     static final String ROUTED_RPC_REG_CONVERTER_NAME = "org.opendaylight.blueprint.RoutedRpcRegConverter";
58     static final String DOM_RPC_PROVIDER_SERVICE_NAME = "org.opendaylight.blueprint.DOMRpcProviderService";
59     static final String RPC_REGISTRY_NAME = "org.opendaylight.blueprint.RpcRegistry";
60     static final String BINDING_RPC_PROVIDER_SERVICE_NAME = "org.opendaylight.blueprint.RpcProviderService";
61     static final String SCHEMA_SERVICE_NAME = "org.opendaylight.blueprint.SchemaService";
62     static final String NOTIFICATION_SERVICE_NAME = "org.opendaylight.blueprint.NotificationService";
63     static final String TYPE_ATTR = "type";
64     static final String UPDATE_STRATEGY_ATTR = "update-strategy";
65
66     private static final Logger LOG = LoggerFactory.getLogger(OpendaylightNamespaceHandler.class);
67     private static final String COMPONENT_PROCESSOR_NAME = ComponentProcessor.class.getName();
68     private static final String RESTART_DEPENDENTS_ON_UPDATES = "restart-dependents-on-updates";
69     private static final String USE_DEFAULT_FOR_REFERENCE_TYPES = "use-default-for-reference-types";
70     private static final String CLUSTERED_APP_CONFIG = "clustered-app-config";
71     private static final String INTERFACE = "interface";
72     private static final String REF_ATTR = "ref";
73     private static final String ID_ATTR = "id";
74     private static final String RPC_SERVICE = "rpc-service";
75     private static final String ACTION_SERVICE = "action-service";
76     private static final String SPECIFIC_SERVICE_REF_LIST = "specific-reference-list";
77     private static final String STATIC_REFERENCE = "static-reference";
78
79     @SuppressWarnings("rawtypes")
80     @Override
81     public Set<Class> getManagedClasses() {
82         return Collections.emptySet();
83     }
84
85     @Override
86     public URL getSchemaLocation(final String namespace) {
87         if (NAMESPACE_1_0_0.equals(namespace)) {
88             URL url = getClass().getResource("/opendaylight-blueprint-ext-1.0.0.xsd");
89             LOG.debug("getSchemaLocation for {} returning URL {}", namespace, url);
90             return url;
91         }
92
93         return null;
94     }
95
96     @Override
97     public Metadata parse(final Element element, final ParserContext context) {
98         LOG.debug("In parse for {}", element);
99
100         if (nodeNameEquals(element, RpcImplementationBean.RPC_IMPLEMENTATION)) {
101             return parseRpcImplementation(element, context);
102         } else if (nodeNameEquals(element, RPC_SERVICE)) {
103             return parseRpcService(element, context);
104         } else if (nodeNameEquals(element, NotificationListenerBean.NOTIFICATION_LISTENER)) {
105             return parseNotificationListener(element, context);
106         } else if (nodeNameEquals(element, CLUSTERED_APP_CONFIG)) {
107             return parseClusteredAppConfig(element, context);
108         } else if (nodeNameEquals(element, SPECIFIC_SERVICE_REF_LIST)) {
109             return parseSpecificReferenceList(element, context);
110         } else if (nodeNameEquals(element, STATIC_REFERENCE)) {
111             return parseStaticReference(element, context);
112         } else if (nodeNameEquals(element, ACTION_SERVICE)) {
113             return parseActionService(element, context);
114         } else if (nodeNameEquals(element, ActionProviderBean.ACTION_PROVIDER)) {
115             return parseActionProvider(element, context);
116         }
117
118         throw new ComponentDefinitionException("Unsupported standalone element: " + element.getNodeName());
119     }
120
121     @Override
122     public ComponentMetadata decorate(final Node node, final ComponentMetadata component, final ParserContext context) {
123         if (node instanceof Attr) {
124             if (nodeNameEquals(node, RESTART_DEPENDENTS_ON_UPDATES)) {
125                 return decorateRestartDependentsOnUpdates((Attr) node, component, context);
126             } else if (nodeNameEquals(node, USE_DEFAULT_FOR_REFERENCE_TYPES)) {
127                 return decorateUseDefaultForReferenceTypes((Attr) node, component, context);
128             } else if (nodeNameEquals(node, TYPE_ATTR)) {
129                 if (component instanceof ServiceReferenceMetadata) {
130                     return decorateServiceReferenceType((Attr) node, component, context);
131                 } else if (component instanceof ServiceMetadata) {
132                     return decorateServiceType((Attr)node, component, context);
133                 }
134
135                 throw new ComponentDefinitionException("Attribute " + node.getNodeName()
136                         + " can only be used on a <reference>, <reference-list> or <service> element");
137             }
138
139             throw new ComponentDefinitionException("Unsupported attribute: " + node.getNodeName());
140         } else {
141             throw new ComponentDefinitionException("Unsupported node type: " + node);
142         }
143     }
144
145     private static ComponentMetadata decorateServiceType(final Attr attr, final ComponentMetadata component,
146             final ParserContext context) {
147         if (!(component instanceof MutableServiceMetadata)) {
148             throw new ComponentDefinitionException("Expected an instanceof MutableServiceMetadata");
149         }
150
151         MutableServiceMetadata service = (MutableServiceMetadata)component;
152
153         LOG.debug("decorateServiceType for {} - adding type property {}", service.getId(), attr.getValue());
154
155         service.addServiceProperty(createValue(context, TYPE_ATTR), createValue(context, attr.getValue()));
156         return component;
157     }
158
159     private static ComponentMetadata decorateServiceReferenceType(final Attr attr, final ComponentMetadata component,
160             final ParserContext context) {
161         if (!(component instanceof MutableServiceReferenceMetadata)) {
162             throw new ComponentDefinitionException("Expected an instanceof MutableServiceReferenceMetadata");
163         }
164
165         // We don't actually need the ComponentProcessor for augmenting the OSGi filter here but we create it
166         // to workaround an issue in Aries where it doesn't use the extended filter unless there's a
167         // Processor or ComponentDefinitionRegistryProcessor registered. This may actually be working as
168         // designed in Aries b/c the extended filter was really added to allow the OSGi filter to be
169         // substituted by a variable via the "cm:property-placeholder" processor. If so, it's a bit funky
170         // but as long as there's at least one processor registered, it correctly uses the extended filter.
171         registerComponentProcessor(context);
172
173         MutableServiceReferenceMetadata serviceRef = (MutableServiceReferenceMetadata)component;
174         String oldFilter = serviceRef.getExtendedFilter() == null ? null :
175             serviceRef.getExtendedFilter().getStringValue();
176
177         String filter;
178         if (Strings.isNullOrEmpty(oldFilter)) {
179             filter = String.format("(type=%s)", attr.getValue());
180         } else {
181             filter = String.format("(&(%s)(type=%s))", oldFilter, attr.getValue());
182         }
183
184         LOG.debug("decorateServiceReferenceType for {} with type {}, old filter: {}, new filter: {}",
185                 serviceRef.getId(), attr.getValue(), oldFilter, filter);
186
187         serviceRef.setExtendedFilter(createValue(context, filter));
188         return component;
189     }
190
191     private static ComponentMetadata decorateRestartDependentsOnUpdates(final Attr attr,
192             final ComponentMetadata component, final ParserContext context) {
193         return enableComponentProcessorProperty(attr, component, context, "restartDependentsOnUpdates");
194     }
195
196     private static ComponentMetadata decorateUseDefaultForReferenceTypes(final Attr attr,
197             final ComponentMetadata component, final ParserContext context) {
198         return enableComponentProcessorProperty(attr, component, context, "useDefaultForReferenceTypes");
199     }
200
201     private static ComponentMetadata enableComponentProcessorProperty(final Attr attr,
202             final ComponentMetadata component, final ParserContext context, final String propertyName) {
203         if (component != null) {
204             throw new ComponentDefinitionException("Attribute " + attr.getNodeName()
205                     + " can only be used on the root <blueprint> element");
206         }
207
208         LOG.debug("Property {} = {}", propertyName, attr.getValue());
209
210         if (!Boolean.parseBoolean(attr.getValue())) {
211             return component;
212         }
213
214         MutableBeanMetadata metadata = registerComponentProcessor(context);
215         metadata.addProperty(propertyName, createValue(context, "true"));
216
217         return component;
218     }
219
220     private static MutableBeanMetadata registerComponentProcessor(final ParserContext context) {
221         ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry();
222         MutableBeanMetadata metadata = (MutableBeanMetadata) registry.getComponentDefinition(COMPONENT_PROCESSOR_NAME);
223         if (metadata == null) {
224             metadata = createBeanMetadata(context, COMPONENT_PROCESSOR_NAME, ComponentProcessor.class, false, true);
225             metadata.setProcessor(true);
226             addBlueprintBundleRefProperty(context, metadata);
227             metadata.addProperty("blueprintContainerRestartService", createServiceRef(context,
228                     BlueprintContainerRestartService.class, null));
229
230             LOG.debug("Registering ComponentProcessor bean: {}", metadata);
231
232             registry.registerComponentDefinition(metadata);
233         }
234
235         return metadata;
236     }
237
238     private static Metadata parseActionProvider(final Element element, final ParserContext context) {
239         registerDomRpcProviderServiceRefBean(context);
240         registerBindingRpcProviderServiceRefBean(context);
241         registerSchemaServiceRefBean(context);
242
243         MutableBeanMetadata metadata = createBeanMetadata(context, context.generateId(), ActionProviderBean.class,
244                 true, true);
245         addBlueprintBundleRefProperty(context, metadata);
246         metadata.addProperty("domRpcProvider", createRef(context, DOM_RPC_PROVIDER_SERVICE_NAME));
247         metadata.addProperty("bindingRpcProvider", createRef(context, BINDING_RPC_PROVIDER_SERVICE_NAME));
248         metadata.addProperty("schemaService", createRef(context, SCHEMA_SERVICE_NAME));
249         metadata.addProperty("interfaceName", createValue(context, element.getAttribute(INTERFACE)));
250
251         if (element.hasAttribute(REF_ATTR)) {
252             metadata.addProperty("implementation", createRef(context, element.getAttribute(REF_ATTR)));
253         }
254
255         LOG.debug("parseActionProvider returning {}", metadata);
256         return metadata;
257     }
258
259
260     private static Metadata parseRpcImplementation(final Element element, final ParserContext context) {
261         registerBindingRpcProviderServiceRefBean(context);
262
263         MutableBeanMetadata metadata = createBeanMetadata(context, context.generateId(), RpcImplementationBean.class,
264                 true, true);
265         addBlueprintBundleRefProperty(context, metadata);
266         metadata.addProperty("rpcProvider", createRef(context, BINDING_RPC_PROVIDER_SERVICE_NAME));
267         metadata.addProperty("implementation", createRef(context, element.getAttribute(REF_ATTR)));
268
269         if (element.hasAttribute(INTERFACE)) {
270             metadata.addProperty("interfaceName", createValue(context, element.getAttribute(INTERFACE)));
271         }
272
273         LOG.debug("parseRpcImplementation returning {}", metadata);
274         return metadata;
275     }
276
277     private static Metadata parseActionService(final Element element, final ParserContext context) {
278         ComponentFactoryMetadata metadata = new ActionServiceMetadata(getId(context, element),
279                 element.getAttribute(INTERFACE));
280
281         LOG.debug("parseActionService returning {}", metadata);
282
283         return metadata;
284     }
285
286     private static Metadata parseRpcService(final Element element, final ParserContext context) {
287         ComponentFactoryMetadata metadata = new RpcServiceMetadata(getId(context, element),
288                 element.getAttribute(INTERFACE));
289
290         LOG.debug("parseRpcService returning {}", metadata);
291
292         return metadata;
293     }
294
295     private static void registerDomRpcProviderServiceRefBean(final ParserContext context) {
296         registerRefBean(context, DOM_RPC_PROVIDER_SERVICE_NAME, DOMRpcProviderService.class);
297     }
298
299     private static void registerBindingRpcProviderServiceRefBean(final ParserContext context) {
300         registerRefBean(context, BINDING_RPC_PROVIDER_SERVICE_NAME, RpcProviderService.class);
301     }
302
303     private static void registerSchemaServiceRefBean(final ParserContext context) {
304         registerRefBean(context, SCHEMA_SERVICE_NAME, DOMSchemaService.class);
305     }
306
307     private static void registerRefBean(final ParserContext context, final String name, final Class<?> clazz) {
308         ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry();
309         if (registry.getComponentDefinition(name) == null) {
310             MutableReferenceMetadata metadata = createServiceRef(context, clazz, null);
311             metadata.setId(name);
312             registry.registerComponentDefinition(metadata);
313         }
314     }
315
316     private static Metadata parseNotificationListener(final Element element, final ParserContext context) {
317         registerNotificationServiceRefBean(context);
318
319         MutableBeanMetadata metadata = createBeanMetadata(context, context.generateId(), NotificationListenerBean.class,
320                 true, true);
321         addBlueprintBundleRefProperty(context, metadata);
322         metadata.addProperty("notificationService", createRef(context, NOTIFICATION_SERVICE_NAME));
323         metadata.addProperty("notificationListener", createRef(context, element.getAttribute(REF_ATTR)));
324
325         LOG.debug("parseNotificationListener returning {}", metadata);
326
327         return metadata;
328     }
329
330     private static void registerNotificationServiceRefBean(final ParserContext context) {
331         ComponentDefinitionRegistry registry = context.getComponentDefinitionRegistry();
332         if (registry.getComponentDefinition(NOTIFICATION_SERVICE_NAME) == null) {
333             MutableReferenceMetadata metadata = createServiceRef(context, NotificationService.class, null);
334             metadata.setId(NOTIFICATION_SERVICE_NAME);
335             registry.registerComponentDefinition(metadata);
336         }
337     }
338
339     private static Metadata parseClusteredAppConfig(final Element element, final ParserContext context) {
340         LOG.debug("parseClusteredAppConfig");
341
342         // Find the default-config child element representing the default app config XML, if present.
343         Element defaultConfigElement = null;
344         NodeList children = element.getChildNodes();
345         for (int i = 0; i < children.getLength(); i++) {
346             Node child = children.item(i);
347             if (nodeNameEquals(child, DataStoreAppConfigMetadata.DEFAULT_CONFIG)) {
348                 defaultConfigElement = (Element) child;
349                 break;
350             }
351         }
352
353         Element defaultAppConfigElement = null;
354         if (defaultConfigElement != null) {
355             // Find the CDATA element containing the default app config XML.
356             children = defaultConfigElement.getChildNodes();
357             for (int i = 0; i < children.getLength(); i++) {
358                 Node child = children.item(i);
359                 if (child.getNodeType() == Node.CDATA_SECTION_NODE) {
360                     defaultAppConfigElement = parseXML(DataStoreAppConfigMetadata.DEFAULT_CONFIG,
361                             child.getTextContent());
362                     break;
363                 }
364             }
365         }
366
367         return new DataStoreAppConfigMetadata(getId(context, element), element.getAttribute(
368                 DataStoreAppConfigMetadata.BINDING_CLASS), element.getAttribute(
369                         DataStoreAppConfigMetadata.LIST_KEY_VALUE), element.getAttribute(
370                         DataStoreAppConfigMetadata.DEFAULT_CONFIG_FILE_NAME), parseUpdateStrategy(
371                         element.getAttribute(UPDATE_STRATEGY_ATTR)), defaultAppConfigElement);
372     }
373
374     private static UpdateStrategy parseUpdateStrategy(final String updateStrategyValue) {
375         if (Strings.isNullOrEmpty(updateStrategyValue)
376                 || updateStrategyValue.equalsIgnoreCase(UpdateStrategy.RELOAD.name())) {
377             return UpdateStrategy.RELOAD;
378         } else if (updateStrategyValue.equalsIgnoreCase(UpdateStrategy.NONE.name())) {
379             return UpdateStrategy.NONE;
380         } else {
381             LOG.warn("update-strategy {} not supported, using reload", updateStrategyValue);
382             return UpdateStrategy.RELOAD;
383         }
384     }
385
386     private static Metadata parseSpecificReferenceList(final Element element, final ParserContext context) {
387         ComponentFactoryMetadata metadata = new SpecificReferenceListMetadata(getId(context, element),
388                 element.getAttribute(INTERFACE));
389
390         LOG.debug("parseSpecificReferenceList returning {}", metadata);
391
392         return metadata;
393     }
394
395     private static Metadata parseStaticReference(final Element element, final ParserContext context) {
396         ComponentFactoryMetadata metadata = new StaticReferenceMetadata(getId(context, element),
397                 element.getAttribute(INTERFACE));
398
399         LOG.debug("parseStaticReference returning {}", metadata);
400
401         return metadata;
402     }
403
404     private static Element parseXML(final String name, final String xml) {
405         try {
406             return UntrustedXML.newDocumentBuilder().parse(new InputSource(new StringReader(xml))).getDocumentElement();
407         } catch (SAXException | IOException e) {
408             throw new ComponentDefinitionException(String.format("Error %s parsing XML: %s", name, xml), e);
409         }
410     }
411
412     private static ValueMetadata createValue(final ParserContext context, final String value) {
413         MutableValueMetadata metadata = context.createMetadata(MutableValueMetadata.class);
414         metadata.setStringValue(value);
415         return metadata;
416     }
417
418     private static MutableReferenceMetadata createServiceRef(final ParserContext context, final Class<?> cls,
419             final String filter) {
420         MutableReferenceMetadata metadata = context.createMetadata(MutableReferenceMetadata.class);
421         metadata.setRuntimeInterface(cls);
422         metadata.setInterface(cls.getName());
423         metadata.setActivation(ReferenceMetadata.ACTIVATION_EAGER);
424         metadata.setAvailability(ReferenceMetadata.AVAILABILITY_MANDATORY);
425
426         if (filter != null) {
427             metadata.setFilter(filter);
428         }
429
430         return metadata;
431     }
432
433     private static RefMetadata createRef(final ParserContext context, final String id) {
434         MutableRefMetadata metadata = context.createMetadata(MutableRefMetadata.class);
435         metadata.setComponentId(id);
436         return metadata;
437     }
438
439     private static String getId(final ParserContext context, final Element element) {
440         if (element.hasAttribute(ID_ATTR)) {
441             return element.getAttribute(ID_ATTR);
442         } else {
443             return context.generateId();
444         }
445     }
446
447     private static boolean nodeNameEquals(final Node node, final String name) {
448         return name.equals(node.getNodeName()) || name.equals(node.getLocalName());
449     }
450
451     private static void addBlueprintBundleRefProperty(final ParserContext context, final MutableBeanMetadata metadata) {
452         metadata.addProperty("bundle", createRef(context, "blueprintBundle"));
453     }
454
455     private static MutableBeanMetadata createBeanMetadata(final ParserContext context, final String id,
456             final Class<?> runtimeClass, final boolean initMethod, final boolean destroyMethod) {
457         MutableBeanMetadata metadata = context.createMetadata(MutableBeanMetadata.class);
458         metadata.setId(id);
459         metadata.setScope(BeanMetadata.SCOPE_SINGLETON);
460         metadata.setActivation(ReferenceMetadata.ACTIVATION_EAGER);
461         metadata.setRuntimeClass(runtimeClass);
462
463         if (initMethod) {
464             metadata.setInitMethod("init");
465         }
466
467         if (destroyMethod) {
468             metadata.setDestroyMethod("destroy");
469         }
470
471         return metadata;
472     }
473 }