From: Robert Varga Date: Thu, 30 Jul 2020 20:10:35 +0000 (+0200) Subject: Add binding adapter components for RPC services X-Git-Tag: v7.0.0~78 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=a261f27ff4e71b35083a765b471242cc8ae53841;hp=45f54e8aa1d777f6c8199383be024a1b8d065279;p=mdsal.git Add binding adapter components for RPC services Rather than operating on service registry directly, create binding adapters as dedicated components. This allows proper provides/requires validation for downstream component users. JIRA: MDSAL-583 Change-Id: If9680d2efe64535551b0c7a3df8058dac89375cf Signed-off-by: Robert Varga (cherry picked from commit 5df23371ec7909983d59c278cfd28ecff66aad78) --- diff --git a/binding/mdsal-binding-dom-adapter/pom.xml b/binding/mdsal-binding-dom-adapter/pom.xml index 83e274c819..a8f73ad7ff 100644 --- a/binding/mdsal-binding-dom-adapter/pom.xml +++ b/binding/mdsal-binding-dom-adapter/pom.xml @@ -99,6 +99,18 @@ + + org.apache.felix + maven-bundle-plugin + true + + org.opendaylight.mdsal.binding.dom.adapter + + + <_dsannotations-options>norequirements + + + maven-jar-plugin diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AbstractAdaptedService.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AbstractAdaptedService.java new file mode 100644 index 0000000000..385af93fbe --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AbstractAdaptedService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.adapter.osgi; + +import static com.google.common.base.Verify.verifyNotNull; +import static java.util.Objects.requireNonNull; + +import java.util.Map; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.mdsal.binding.api.BindingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +abstract class AbstractAdaptedService { + private static final Logger LOG = LoggerFactory.getLogger(AbstractAdaptedService.class); + static final @NonNull String DELEGATE = + "org.opendaylight.mdsal.binding.dom.adapter.osgi.AbstractAdaptedService.DELEGATE"; + + private final Class bindingService; + + private @Nullable B delegate; + + AbstractAdaptedService(final Class bindingService) { + this.bindingService = requireNonNull(bindingService); + } + + final void start(final Map properties) { + delegate = bindingService.cast(verifyNotNull(properties.get(DELEGATE))); + LOG.info("Binding/DOM adapter for {} activated", bindingService.getSimpleName()); + } + + final void stop() { + delegate = null; + LOG.info("Binding/DOM adapter for {} deactivated", bindingService.getSimpleName()); + } + + final @NonNull B delegate() { + return verifyNotNull(delegate); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AbstractAdaptingTracker.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AbstractAdaptingTracker.java new file mode 100644 index 0000000000..bfd368d628 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AbstractAdaptingTracker.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.mdsal.binding.dom.adapter.osgi; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.binding.api.BindingService; +import org.opendaylight.mdsal.dom.api.DOMService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A ServiceTracker which adapts a DOMService to a BindingService. + * + * @param DOMService type + * @param BindingService type + * @author Robert Varga + */ +abstract class AbstractAdaptingTracker + extends ServiceTracker { + private static final Logger LOG = LoggerFactory.getLogger(AbstractAdaptingTracker.class); + + private final Function bindingFactory; + final @NonNull Class bindingClass; + + AbstractAdaptingTracker(final BundleContext ctx, final Class domClass, final Class bindingClass, + final Function bindingFactory) { + super(ctx, domClass, null); + this.bindingClass = requireNonNull(bindingClass); + this.bindingFactory = requireNonNull(bindingFactory); + } + + @Override + public final void open(final boolean trackAllServices) { + LOG.debug("Starting tracker for {}", bindingClass.getName()); + super.open(trackAllServices); + LOG.debug("Tracker for {} started", bindingClass.getName()); + } + + @Override + public final T addingService(final ServiceReference reference) { + if (reference == null) { + LOG.debug("Null reference for {}, ignoring it", bindingClass.getName()); + return null; + } + if (reference.getProperty(ServiceProperties.IGNORE_PROP) != null) { + LOG.debug("Ignoring reference {} due to {}", reference, ServiceProperties.IGNORE_PROP); + return null; + } + + final D dom = context.getService(reference); + if (dom == null) { + LOG.debug("Could not get {} service from {}, ignoring it", bindingClass.getName(), reference); + return null; + } + + return addingService(reference, dom, bindingFactory.apply(dom)); + } + + abstract @NonNull T addingService(@NonNull ServiceReference reference, @NonNull D dom, @NonNull B binding); + + @Override + public final void modifiedService(final ServiceReference reference, final T service) { + if (service != null && reference != null) { + updatedService(reference, service); + } + } + + abstract void updatedService(@NonNull ServiceReference reference, @NonNull T service); + + @Override + public final void removedService(final ServiceReference reference, final T service) { + if (service != null) { + context.ungetService(reference); + removedService(service); + LOG.debug("Unregistered service {}", service); + } + } + + abstract void removedService(@NonNull T service); + +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingComponentTracker.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingComponentTracker.java new file mode 100644 index 0000000000..86a8c09dc2 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingComponentTracker.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.adapter.osgi; + +import static java.util.Objects.requireNonNull; + +import java.util.function.Function; +import org.opendaylight.mdsal.binding.api.BindingService; +import org.opendaylight.mdsal.dom.api.DOMService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentFactory; +import org.osgi.service.component.ComponentInstance; + +final class AdaptingComponentTracker + extends AbstractAdaptingTracker> { + static final class ComponentHolder { + final B binding; + ComponentInstance component; + + ComponentHolder(final B binding, final ComponentInstance component) { + this.binding = requireNonNull(binding); + this.component = requireNonNull(component); + } + } + + private final ComponentFactory componentFactory; + + AdaptingComponentTracker(final BundleContext ctx, final Class domClass, final Class bindingClass, + final Function bindingFactory, final ComponentFactory componentFactory) { + super(ctx, domClass, bindingClass, bindingFactory); + this.componentFactory = requireNonNull(componentFactory); + } + + @Override + ComponentHolder addingService(final ServiceReference reference, final D dom, final B binding) { + return new ComponentHolder<>(binding, componentFactory.newInstance(Dict.fromReference(reference, binding))); + } + + @Override + void removedService(final ComponentHolder service) { + service.component.dispose(); + } + + @Override + void updatedService(final ServiceReference reference, final ComponentHolder service) { + service.component.dispose(); + service.component = componentFactory.newInstance(Dict.fromReference(reference, service.binding)); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingTracker.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingTracker.java index 1858c2e0ea..5047134f68 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingTracker.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/AdaptingTracker.java @@ -7,15 +7,12 @@ */ package org.opendaylight.mdsal.binding.dom.adapter.osgi; -import static java.util.Objects.requireNonNull; - import java.util.function.Function; import org.opendaylight.mdsal.binding.api.BindingService; import org.opendaylight.mdsal.dom.api.DOMService; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; -import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,43 +24,16 @@ import org.slf4j.LoggerFactory; * @author Robert Varga */ final class AdaptingTracker - extends ServiceTracker> { + extends AbstractAdaptingTracker> { private static final Logger LOG = LoggerFactory.getLogger(AdaptingTracker.class); - private final Function bindingFactory; - private final Class bindingClass; - AdaptingTracker(final BundleContext ctx, final Class domClass, final Class bindingClass, - final Function bindingFactory) { - super(ctx, domClass, null); - this.bindingClass = requireNonNull(bindingClass); - this.bindingFactory = requireNonNull(bindingFactory); - } - - @Override - public void open(final boolean trackAllServices) { - LOG.debug("Starting tracker for {}", bindingClass.getName()); - super.open(trackAllServices); - LOG.debug("Tracker for {} started", bindingClass.getName()); + final Function bindingFactory) { + super(ctx, domClass, bindingClass, bindingFactory); } @Override - public ServiceRegistration addingService(final ServiceReference reference) { - if (reference == null) { - LOG.debug("Null reference for {}, ignoring it", bindingClass.getName()); - return null; - } - if (reference.getProperty(ServiceProperties.IGNORE_PROP) != null) { - LOG.debug("Ignoring reference {} due to {}", reference, ServiceProperties.IGNORE_PROP); - return null; - } - - final D dom = context.getService(reference); - if (dom == null) { - LOG.debug("Could not get {} service from {}, ignoring it", bindingClass.getName(), reference); - return null; - } - final B binding = bindingFactory.apply(dom); + ServiceRegistration addingService(final ServiceReference reference, final D dom, final B binding) { final Dict props = Dict.fromReference(reference); final ServiceRegistration reg = context.registerService(bindingClass, binding, props); LOG.debug("Registered {} adapter {} of {} with {} as {}", bindingClass.getName(), binding, dom, props, reg); @@ -71,20 +41,14 @@ final class AdaptingTracker } @Override - public void modifiedService(final ServiceReference reference, final ServiceRegistration service) { - if (service != null && reference != null) { - final Dict newProps = Dict.fromReference(reference); - LOG.debug("Updating service {} with properties {}", service, newProps); - service.setProperties(newProps); - } + void removedService(final ServiceRegistration service) { + service.unregister(); } @Override - public void removedService(final ServiceReference reference, final ServiceRegistration service) { - if (service != null) { - context.ungetService(reference); - service.unregister(); - LOG.debug("Unregistered service {}", service); - } + void updatedService(final ServiceReference reference, final ServiceRegistration service) { + final Dict newProps = Dict.fromReference(reference); + LOG.debug("Updating service {} with properties {}", service, newProps); + service.setProperties(newProps); } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/Dict.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/Dict.java index ab4fa7faef..d30f5ffc24 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/Dict.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/Dict.java @@ -15,6 +15,7 @@ import java.util.Enumeration; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.mdsal.binding.api.BindingService; import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +37,18 @@ final class Dict extends Dictionary { return EMPTY; } - final Map props = Maps.newHashMapWithExpectedSize(keys.length); + return new Dict(populateProperties(ref, keys, 0)); + } + + static Dict fromReference(final ServiceReference ref, final BindingService service) { + final Map props = populateProperties(ref, ref.getPropertyKeys(), 1); + props.put(AbstractAdaptedService.DELEGATE, service); + return new Dict(props); + } + + private static Map populateProperties(final ServiceReference ref, final String[] keys, + final int extra) { + final Map props = Maps.newHashMapWithExpectedSize(keys.length + extra); for (String key : keys) { // Ignore properties with our prefix: we are not exporting those if (!key.startsWith(ServiceProperties.PREFIX)) { @@ -61,7 +73,7 @@ final class Dict extends Dictionary { } } - return new Dict(props); + return props; } @Override diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/DynamicBindingAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/DynamicBindingAdapter.java index 3109e46759..82e631559a 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/DynamicBindingAdapter.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/DynamicBindingAdapter.java @@ -31,6 +31,7 @@ import org.opendaylight.mdsal.dom.api.DOMRpcProviderService; import org.opendaylight.mdsal.dom.api.DOMRpcService; import org.opendaylight.mdsal.dom.api.DOMService; import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentFactory; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; @@ -49,10 +50,14 @@ import org.slf4j.LoggerFactory; public final class DynamicBindingAdapter { private static final Logger LOG = LoggerFactory.getLogger(DynamicBindingAdapter.class); - private List> trackers = ImmutableList.of(); + private List> trackers = ImmutableList.of(); @Reference AdapterFactory factory = null; + @Reference(target = "(component.factory=" + OSGiRpcConsumerRegistry.FACTORY_NAME + ")") + ComponentFactory rpcConsumerRegistryFactory = null; + @Reference(target = "(component.factory=" + OSGiRpcProviderService.FACTORY_NAME + ")") + ComponentFactory rpcProviderServiceFactory = null; @Activate void activate(final BundleContext ctx) { @@ -65,10 +70,10 @@ public final class DynamicBindingAdapter { factory::createNotificationService), new AdaptingTracker<>(ctx, DOMNotificationPublishService.class, NotificationPublishService.class, factory::createNotificationPublishService), - new AdaptingTracker<>(ctx, DOMRpcService.class, RpcConsumerRegistry.class, - factory::createRpcConsumerRegistry), - new AdaptingTracker<>(ctx, DOMRpcProviderService.class, RpcProviderService.class, - factory::createRpcProviderService), + new AdaptingComponentTracker<>(ctx, DOMRpcService.class, RpcConsumerRegistry.class, + factory::createRpcConsumerRegistry, rpcConsumerRegistryFactory), + new AdaptingComponentTracker<>(ctx, DOMRpcProviderService.class, RpcProviderService.class, + factory::createRpcProviderService, rpcProviderServiceFactory), new AdaptingTracker<>(ctx, DOMActionService.class, ActionService.class, factory::createActionService), new AdaptingTracker<>(ctx, DOMActionProviderService.class, ActionProviderService.class, factory::createActionProviderService)); @@ -82,7 +87,7 @@ public final class DynamicBindingAdapter { void deactivate() { LOG.debug("Stopping {} DOMService trackers", trackers.size()); if (!trackers.isEmpty()) { - trackers.forEach(AdaptingTracker::close); + trackers.forEach(AbstractAdaptingTracker::close); LOG.info("{} DOMService trackers stopped", trackers.size()); } trackers = ImmutableList.of(); diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiRpcConsumerRegistry.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiRpcConsumerRegistry.java new file mode 100644 index 0000000000..fcf9dbca54 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiRpcConsumerRegistry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.adapter.osgi; + +import com.google.common.annotations.Beta; +import java.util.Map; +import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; + +@Beta +@Component(factory = OSGiRpcConsumerRegistry.FACTORY_NAME) +public final class OSGiRpcConsumerRegistry extends AbstractAdaptedService + implements RpcConsumerRegistry { + // OSGi DS Component Factory name + static final String FACTORY_NAME = "org.opendaylight.mdsal.binding.dom.adapter.osgi.OSGiRpcConsumerRegistry"; + + public OSGiRpcConsumerRegistry() { + super(RpcConsumerRegistry.class); + } + + @Override + public T getRpcService(final Class serviceInterface) { + return delegate().getRpcService(serviceInterface); + } + + @Activate + void activate(final Map properties) { + start(properties); + } + + @Deactivate + void deactivate() { + stop(); + } +} diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiRpcProviderService.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiRpcProviderService.java new file mode 100644 index 0000000000..77ff3cde94 --- /dev/null +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/OSGiRpcProviderService.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.adapter.osgi; + +import com.google.common.annotations.Beta; +import java.util.Map; +import java.util.Set; +import org.opendaylight.mdsal.binding.api.RpcProviderService; +import org.opendaylight.yangtools.concepts.ObjectRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; + +@Beta +@Component(factory = OSGiRpcProviderService.FACTORY_NAME) +public final class OSGiRpcProviderService extends AbstractAdaptedService + implements RpcProviderService { + // OSGi DS Component Factory name + static final String FACTORY_NAME = "org.opendaylight.mdsal.binding.dom.adapter.osgi.OSGiRpcProviderService"; + + public OSGiRpcProviderService() { + super(RpcProviderService.class); + } + + @Override + public ObjectRegistration registerRpcImplementation(final Class type, + final T implementation) { + return delegate().registerRpcImplementation(type, implementation); + } + + @Override + public ObjectRegistration registerRpcImplementation(final Class type, + final T implementation, final Set> paths) { + return delegate().registerRpcImplementation(type, implementation, paths); + } + + @Activate + void activate(final Map properties) { + start(properties); + } + + @Deactivate + void deactivate() { + stop(); + } +}