From: Alessandro Boch Date: Mon, 16 Dec 2013 20:07:09 +0000 (+0000) Subject: Merge "Improve iteration over subnets map by using entrySet instead of keyset. It... X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~184 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=523c3f0629438462c5bb7be4adcaf7103a3f7ea6;hp=92cd19a5961d6de45dd3ea1c8f7c2b4be108cbae Merge "Improve iteration over subnets map by using entrySet instead of keyset. It is more efficient to iterate on the entrySet because it avoids the Map.get(key) lookup." --- diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index e519a04a57..35e160e827 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -285,6 +285,11 @@ ietf-netconf-monitoring ${netconf.version} + + ${project.groupId} + ietf-netconf-monitoring-extension + ${netconf.version} + org.opendaylight.controller config-persister-impl diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index 6f1b2c0c10..4f2b255afd 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -428,6 +428,26 @@ org.apache.felix maven-bundle-plugin + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/config + ${project.build.directory}/generated-sources/sal + + + + + org.apache.maven.plugins maven-jar-plugin diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcAvailabilityListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcAvailabilityListener.java new file mode 100644 index 0000000000..e25b93918a --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/RpcAvailabilityListener.java @@ -0,0 +1,5 @@ +package org.opendaylight.controller.sal.binding.api; + +public interface RpcAvailabilityListener { + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java index 74b6ad8a23..01dc6b8c0c 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java @@ -16,7 +16,7 @@ import java.util.concurrent.ScheduledExecutorService; import org.opendaylight.controller.config.yang.md.sal.binding.statistics.DataBrokerRuntimeMXBeanImpl; import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter; import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector; +import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.data.DataProviderService; @@ -63,7 +63,7 @@ public final class DataBrokerImplModule extends BindingIndependentMappingService mappingService = getMappingServiceDependency(); if (domBroker != null && mappingService != null) { - BindingIndependentDataServiceConnector runtimeMapping = new BindingIndependentDataServiceConnector(); + BindingIndependentConnector runtimeMapping = new BindingIndependentConnector(); runtimeMapping.setMappingService(mappingService); runtimeMapping.setBaDataService(dataBindingBroker); domBroker.registerProvider(runtimeMapping, getBundleContext()); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java index 1bf15c182f..99b7ed8acf 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java @@ -11,6 +11,7 @@ package org.opendaylight.controller.config.yang.md.sal.binding.impl; import javassist.ClassPool; +import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl; import org.osgi.framework.BundleContext; @@ -50,8 +51,7 @@ public final class RuntimeMappingModule extends @Override public java.lang.AutoCloseable createInstance() { RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl(); - ClassPool pool = new ClassPool(); // Should be default singleton - service.setPool(pool); + service.setPool(SingletonHolder.CLASS_POOL); service.start(getBundleContext()); return service; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.xtend index f0f92da18e..dff0d215b2 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeHelper.xtend @@ -76,7 +76,6 @@ class RuntimeCodeHelper { if (field == null) throw new UnsupportedOperationException( "Unable to set routing table. Table field does not exists"); field.set(target,routingTable); - } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java new file mode 100644 index 0000000000..780d0bd4c7 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.java @@ -0,0 +1,161 @@ +package org.opendaylight.controller.sal.binding.codegen.impl; + +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; +import org.opendaylight.controller.sal.binding.spi.RpcRouter; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import static org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.*; + +import java.util.Map; +import java.util.Set; +import java.util.HashMap; + +import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable; +import org.opendaylight.yangtools.yang.binding.DataContainer; +import org.opendaylight.yangtools.yang.binding.RpcImplementation; +import org.opendaylight.controller.md.sal.common.api.routing.MutableRoutingTable; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChange; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.util.ListenerRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class RpcRouterCodegenInstance implements // + RpcRouter, RouteChangeListener, InstanceIdentifier> { + + private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class); + + private T defaultService; + + private final Class serviceType; + + private final T invocationProxy; + + private final Set> contexts; + + private final ListenerRegistry, InstanceIdentifier>> listeners; + + private final Map, RpcRoutingTableImpl> routingTables; + + public RpcRouterCodegenInstance(Class type, T routerImpl, Set> contexts, + Set> inputs) { + this.listeners = ListenerRegistry.create(); + this.serviceType = type; + this.invocationProxy = routerImpl; + this.contexts = ImmutableSet.copyOf(contexts); + Map, RpcRoutingTableImpl> mutableRoutingTables = new HashMap<>(); + for (Class ctx : contexts) { + RpcRoutingTableImpl table = new RpcRoutingTableImpl<>(ctx); + Map invokerView = table.getRoutes(); + setRoutingTable((RpcService) invocationProxy, ctx, invokerView); + mutableRoutingTables.put(ctx, table); + table.registerRouteChangeListener(this); + } + this.routingTables = ImmutableMap.copyOf(mutableRoutingTables); + } + + @Override + public Class getServiceType() { + return serviceType; + } + + @Override + public T getInvocationProxy() { + return invocationProxy; + } + + @Override + @SuppressWarnings("unchecked") + public RpcRoutingTable getRoutingTable(Class routeContext) { + return (RpcRoutingTable) routingTables.get(routeContext); + } + + @Override + public T getDefaultService() { + return defaultService; + } + + @Override + public Set> getContexts() { + return contexts; + } + + @Override + public , InstanceIdentifier>> ListenerRegistration registerRouteChangeListener( + L listener) { + return listeners.registerWithType(listener); + } + + @Override + public void onRouteChange(RouteChange, InstanceIdentifier> change) { + for (ListenerRegistration, InstanceIdentifier>> listener : listeners) { + try { + listener.getInstance().onRouteChange(change); + } catch (Exception e) { + LOG.error("Error occured during invoker listener {}", listener.getInstance(), e); + } + } + } + + @Override + public T getService(Class context, InstanceIdentifier path) { + return routingTables.get(context).getRoute(path); + } + + @Override + public RoutedRpcRegistration addRoutedRpcImplementation(T service) { + return new RoutedRpcRegistrationImpl(service); + } + + @Override + public RpcRegistration registerDefaultService(T service) { + // TODO Auto-generated method stub + return null; + } + + private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration implements RoutedRpcRegistration { + + public RoutedRpcRegistrationImpl(T instance) { + super(instance); + } + + @Override + public Class getServiceType() { + return serviceType; + } + + @Override + public void registerPath(Class context, InstanceIdentifier path) { + routingTables.get(context).updateRoute(path, getInstance()); + } + + @Override + public void unregisterPath(Class context, InstanceIdentifier path) { + routingTables.get(context).removeRoute(path, getInstance()); + + } + + @Override + public void registerInstance(Class context, InstanceIdentifier instance) { + registerPath(context, instance); + } + + @Override + public void unregisterInstance(Class context, InstanceIdentifier instance) { + unregisterPath(context, instance); + } + + @Override + protected void removeRegistration() { + + } + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.xtend deleted file mode 100644 index b6dcde19ee..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRouterCodegenInstance.xtend +++ /dev/null @@ -1,64 +0,0 @@ -package org.opendaylight.controller.sal.binding.codegen.impl - -import org.opendaylight.yangtools.yang.binding.RpcService -import org.opendaylight.controller.sal.binding.spi.RpcRouter -import org.opendaylight.yangtools.yang.binding.BaseIdentity -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier -import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper.* -import java.util.Set -import java.util.HashMap -import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable -import org.opendaylight.yangtools.yang.binding.DataContainer -import org.opendaylight.yangtools.yang.binding.RpcImplementation - -class RpcRouterCodegenInstance implements RpcRouter { - - @Property - val T invocationProxy - - @Property - val RpcImplementation invokerDelegate; - - @Property - val Class serviceType - - @Property - val Set> contexts - - @Property - val Set> supportedInputs; - - val routingTables = new HashMap, RpcRoutingTableImpl>; - - @Property - var T defaultService - - new(Class type, T routerImpl, Set> contexts, - Set> inputs) { - _serviceType = type - _invocationProxy = routerImpl - _invokerDelegate = routerImpl as RpcImplementation - _contexts = contexts - _supportedInputs = inputs; - - for (ctx : contexts) { - val table = XtendHelper.createRoutingTable(ctx) - invocationProxy.setRoutingTable(ctx, table.routes); - routingTables.put(ctx, table); - } - } - - override getRoutingTable(Class table) { - routingTables.get(table) as RpcRoutingTable - } - - override getService(Class context, InstanceIdentifier path) { - val table = getRoutingTable(context); - return table.getRoute(path); - } - - override invoke(Class type, T input) { - return invokerDelegate.invoke(type, input); - } - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.java new file mode 100644 index 0000000000..f9592351f6 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.java @@ -0,0 +1,125 @@ +package org.opendaylight.controller.sal.binding.codegen.impl; + +import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; +import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils; +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.Mutable; + +class RpcRoutingTableImpl // +implements // + Mutable, // + RpcRoutingTable, // + RouteChangePublisher, InstanceIdentifier> { + + private final Class identifier; + private final ConcurrentMap, S> routes; + private final Map, S> unmodifiableRoutes; + + private RouteChangeListener, InstanceIdentifier> listener; + private S defaultRoute; + + public RpcRoutingTableImpl(Class identifier) { + super(); + this.identifier = identifier; + this.routes = new ConcurrentHashMap<>(); + this.unmodifiableRoutes = Collections.unmodifiableMap(routes); + } + + @Override + public void setDefaultRoute(S target) { + defaultRoute = target; + } + + @Override + public S getDefaultRoute() { + return defaultRoute; + } + + @Override + public , InstanceIdentifier>> ListenerRegistration registerRouteChangeListener( + L listener) { + return (ListenerRegistration) new SingletonListenerRegistration(listener); + } + + @Override + public Class getIdentifier() { + return identifier; + } + + @Override + @SuppressWarnings("unchecked") + public void updateRoute(InstanceIdentifier path, S service) { + S previous = this.routes.put(path, service); + @SuppressWarnings("rawtypes") + RouteChangeListener listenerCapture = listener; + if (previous == null && listenerCapture != null) { + listenerCapture.onRouteChange(RoutingUtils.announcementChange(identifier, path)); + } + } + + + @Override + @SuppressWarnings("unchecked") + public void removeRoute(InstanceIdentifier path) { + S previous = this.routes.remove(path); + @SuppressWarnings("rawtypes") + RouteChangeListener listenerCapture = listener; + if (previous != null && listenerCapture != null) { + listenerCapture.onRouteChange(RoutingUtils.removalChange(identifier, path)); + } + } + + public void removeRoute(InstanceIdentifier path, S service) { + @SuppressWarnings("rawtypes") + RouteChangeListener listenerCapture = listener; + if (routes.remove(path, service) && listenerCapture != null) { + listenerCapture.onRouteChange(RoutingUtils.removalChange(identifier, path)); + } + } + + @Override + public S getRoute(InstanceIdentifier nodeInstance) { + S route = routes.get(nodeInstance); + if (route != null) { + return route; + } + return getDefaultRoute(); + } + + @Override + public Map, S> getRoutes() { + return unmodifiableRoutes; + } + + protected void removeAllReferences(S service) { + + } + + private class SingletonListenerRegistration, InstanceIdentifier>> extends + AbstractObjectRegistration + implements ListenerRegistration { + + public SingletonListenerRegistration(L instance) { + super(instance); + listener = instance; + } + + @Override + protected void removeRegistration() { + listener = null; + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.xtend deleted file mode 100644 index 116a8177f9..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RpcRoutingTableImpl.xtend +++ /dev/null @@ -1,49 +0,0 @@ -package org.opendaylight.controller.sal.binding.codegen.impl - -import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable -import org.opendaylight.yangtools.yang.binding.BaseIdentity -import org.opendaylight.yangtools.yang.binding.RpcService -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier -import java.util.Map -import org.opendaylight.yangtools.yang.binding.DataObject -import java.util.HashMap - -class RpcRoutingTableImpl implements RpcRoutingTable{ - - @Property - val Class identifier; - - @Property - var S defaultRoute; - - @Property - val Map,S> routes; - - new(Class ident, Map,S> route) { - _identifier = ident - _routes = route - } - - new(Class ident) { - _identifier = ident - _routes = new HashMap - } - - - override getRoute(InstanceIdentifier nodeInstance) { - val ret = routes.get(nodeInstance); - if(ret !== null) { - return ret; - } - return defaultRoute; - } - - override removeRoute(InstanceIdentifier path) { - routes.remove(path); - } - - @SuppressWarnings("rawtypes") - override updateRoute(InstanceIdentifier path, S service) { - routes.put(path as InstanceIdentifier,service); - } -} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend index 90fcbd99aa..7ebcf02e41 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend @@ -27,7 +27,7 @@ import org.opendaylight.yangtools.yang.binding.Notification import static extension org.opendaylight.controller.sal.binding.codegen.YangtoolsMappingHelper.* import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.* import java.util.HashSet -import static org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils.* +import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.* import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory.NotificationInvoker import java.util.Set @@ -37,6 +37,8 @@ import org.opendaylight.yangtools.yang.binding.annotations.QName import org.opendaylight.yangtools.yang.binding.DataContainer import org.opendaylight.yangtools.yang.binding.RpcImplementation import org.opendaylight.controller.sal.binding.codegen.util.JavassistUtils +import org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils +import javassist.LoaderClassPath class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory { @@ -45,40 +47,70 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co val extension JavassistUtils utils; val Map, RuntimeGeneratedInvokerPrototype> invokerClasses; - public new(ClassPool pool) { + + new(ClassPool pool) { classPool = pool; utils = new JavassistUtils(pool); invokerClasses = new WeakHashMap(); BROKER_NOTIFICATION_LISTENER = org.opendaylight.controller.sal.binding.api.NotificationListener.asCtClass; + pool.appendClassPath(new LoaderClassPath(RpcService.classLoader)); } override getDirectProxyFor(Class iface) { - val supertype = iface.asCtClass - val targetCls = createClass(iface.directProxyName, supertype) [ - field(DELEGATE_FIELD, iface); - implementMethodsFrom(supertype) [ - body = ''' - { - if(«DELEGATE_FIELD» == null) { + val T instance = withClassLoaderAndLock(iface.classLoader,lock) [| + val proxyName = iface.directProxyName; + val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName) + if(potentialClass != null) { + return potentialClass.newInstance as T; + } + val supertype = iface.asCtClass + val createdCls = createClass(iface.directProxyName, supertype) [ + field(DELEGATE_FIELD, iface); + implementsType(RpcImplementation.asCtClass) + implementMethodsFrom(supertype) [ + body = ''' + { + if(«DELEGATE_FIELD» == null) { + throw new java.lang.IllegalStateException("No provider is processing supplied message"); + } + return ($r) «DELEGATE_FIELD».«it.name»($$); + } + ''' + ] + implementMethodsFrom(RpcImplementation.asCtClass) [ + body = ''' + { throw new java.lang.IllegalStateException("No provider is processing supplied message"); + return ($r) null; } - return ($r) «DELEGATE_FIELD».«it.name»($$); - } - ''' + ''' + ] ] + return createdCls.toClass(iface.classLoader).newInstance as T ] - return targetCls.toClass(iface.classLoader).newInstance as T + return instance; } override getRouterFor(Class iface) { - val instance = >withClassLoaderAndLock(iface.classLoader,lock) [ | + val metadata = withClassLoader(iface.classLoader) [| + val supertype = iface.asCtClass + return supertype.rpcMetadata; + ] + + val instance = withClassLoaderAndLock(iface.classLoader,lock) [ | val supertype = iface.asCtClass - val metadata = supertype.rpcMetadata; + val routerName = iface.routerName; + val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName) + if(potentialClass != null) { + return potentialClass.newInstance as T; + } + val targetCls = createClass(iface.routerName, supertype) [ - addInterface(RpcImplementation.asCtClass) + field(DELEGATE_FIELD, iface) //field(REMOTE_INVOKER_FIELD,iface); + implementsType(RpcImplementation.asCtClass) for (ctx : metadata.contexts) { field(ctx.routingTableField, Map) @@ -105,35 +137,18 @@ class RuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.co } ] implementMethodsFrom(RpcImplementation.asCtClass) [ - switch (name) { - case "getSupportedInputs": - body = ''' - { - throw new java.lang.UnsupportedOperationException("Not implemented yet"); - return ($r) null; - }''' - case "invoke": { - val tmpBody = ''' - { - «FOR input : metadata.supportedInputs SEPARATOR " else "» - «val rpcMetadata = metadata.rpcInputs.get(input)» - if(«input.name».class.equals($1)) { - return ($r) this.«rpcMetadata.methodName»((«input.name») $2); - } - «ENDFOR» - throw new java.lang.IllegalArgumentException("Not supported message type"); - return ($r) null; - } - ''' - body = tmpBody - } + body = ''' + { + throw new java.lang.IllegalStateException("No provider is processing supplied message"); + return ($r) null; } + ''' ] ] - val instance = targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T - return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs); + return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as T + ]; - return instance; + return new RpcRouterCodegenInstance(iface, instance, metadata.contexts,metadata.supportedInputs); } private def RpcServiceMetadata getRpcMetadata(CtClass iface) { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java new file mode 100644 index 0000000000..266293fb6d --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java @@ -0,0 +1,14 @@ +package org.opendaylight.controller.sal.binding.codegen.impl; + +import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator; +import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory; + +import javassist.ClassPool; + +public class SingletonHolder { + + public static final ClassPool CLASS_POOL = new ClassPool(); + public static final org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator RPC_GENERATOR_IMPL = new org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator(CLASS_POOL); + public static final RuntimeCodeGenerator RPC_GENERATOR = RPC_GENERATOR_IMPL; + public static final NotificationInvokerFactory INVOKER_FACTORY = RPC_GENERATOR_IMPL.getInvokerFactory(); +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java index 4b672f1140..f1ba5843ba 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/LazyGeneratedCodecRegistry.java @@ -175,7 +175,7 @@ public class LazyGeneratedCodecRegistry implements // }); return ret; } catch (Exception e) { - LOG.error("Could not find augmentable for {}", augmentation, e); + LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e); return null; } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend index 853e62aa38..1b3acf7674 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/dom/serializer/impl/RuntimeGeneratedMappingServiceImpl.xtend @@ -43,6 +43,11 @@ import java.util.ArrayList import org.opendaylight.yangtools.yang.data.api.Node import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl +import org.opendaylight.yangtools.yang.binding.RpcService +import java.util.Set +import org.opendaylight.yangtools.yang.common.QName +import com.google.common.collect.FluentIterable +import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener, AutoCloseable { @@ -65,6 +70,9 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer @Property val ConcurrentMap typeToSchemaNode = new ConcurrentHashMap(); + + @Property + val ConcurrentMap> serviceTypeToRpc = new ConcurrentHashMap(); val promisedTypeDefinitions = HashMultimap.>create; @@ -85,10 +93,17 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer registry.onModuleContextAdded(schemaContext, entry.key, entry.value); binding.pathToType.putAll(entry.value.childNodes) - //val module = entry.key; + val module = entry.key; val context = entry.value; updateBindingFor(context.childNodes, schemaContext); updateBindingFor(context.cases, schemaContext); + val namespace = BindingGeneratorUtil.moduleNamespaceToPackageName(module); + + if(!module.rpcs.empty) { + val rpcs = FluentIterable.from(module.rpcs).transform[QName].toSet + val serviceClass = new ReferencedTypeImpl(namespace,BindingGeneratorUtil.parseToClassName(module.name)+"Service"); + serviceTypeToRpc.put(serviceClass,rpcs); + } val typedefs = context.typedefs; for (typedef : typedefs.entrySet) { @@ -186,15 +201,7 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer } override dataObjectFromDataDom(InstanceIdentifier path, CompositeNode node) { - return tryDeserialization[ | - if (node == null) { - return null; - } - val targetType = path.targetType - val transformer = registry.getCodecForDataObject(targetType); - val ret = transformer.deserialize(node)?.value as DataObject; - return ret; - ] + dataObjectFromDataDom(path.targetType,node) as DataObject; } override fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) { @@ -243,6 +250,10 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer listenerRegistration = ctx.registerService(SchemaServiceListener, this, new Hashtable()); } } + + override getRpcQNamesFor(Class service) { + return serviceTypeToRpc.get(new ReferencedTypeImpl(service.package.name,service.simpleName)); + } private def getSchemaWithRetry(Type type) { val typeDef = typeToSchemaNode.get(type); @@ -274,5 +285,16 @@ class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingSer override close() throws Exception { listenerRegistration?.unregister(); } + + override dataObjectFromDataDom(Class container, CompositeNode domData) { + return tryDeserialization[ | + if (domData == null) { + return null; + } + val transformer = registry.getCodecForDataObject(container); + val ret = transformer.deserialize(domData)?.value as DataObject; + return ret; + ] + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/BindingAwareBrokerImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/BindingAwareBrokerImpl.xtend index 9381a5a070..8d3545fbbb 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/BindingAwareBrokerImpl.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/BindingAwareBrokerImpl.xtend @@ -13,7 +13,6 @@ import org.opendaylight.yangtools.yang.binding.RpcService import javassist.ClassPool import org.osgi.framework.BundleContext import java.util.Map -import java.util.HashMap import javassist.LoaderClassPath import org.opendaylight.controller.sal.binding.api.BindingAwareBroker import java.util.Hashtable @@ -49,29 +48,14 @@ import java.util.concurrent.Callable import java.util.WeakHashMap import javax.annotation.concurrent.GuardedBy import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry +import org.opendaylight.yangtools.concepts.ListenerRegistration +import org.opendaylight.yangtools.concepts.util.ListenerRegistry -class BindingAwareBrokerImpl implements BindingAwareBroker, RpcProviderRegistry, AutoCloseable { +class BindingAwareBrokerImpl extends RpcProviderRegistryImpl implements BindingAwareBroker, AutoCloseable { private static val log = LoggerFactory.getLogger(BindingAwareBrokerImpl) private InstanceIdentifier root = InstanceIdentifier.builder().toInstance(); - private static val clsPool = ClassPool.getDefault() - public static var RuntimeCodeGenerator generator; - - /** - * Map of all Managed Direct Proxies - * - */ - private val Map, RpcProxyContext> managedProxies = new ConcurrentHashMap(); - - /** - * - * Map of all available Rpc Routers - * - * - */ - private val Map, RpcRouter> rpcRouters = new WeakHashMap(); - @Property private var NotificationProviderService notifyBroker @@ -81,43 +65,15 @@ class BindingAwareBrokerImpl implements BindingAwareBroker, RpcProviderRegistry, @Property var BundleContext brokerBundleContext - ServiceRegistration notifyProviderRegistration - - ServiceRegistration notifyConsumerRegistration - - ServiceRegistration dataProviderRegistration - - ServiceRegistration dataConsumerRegistration - - private val proxyGenerationLock = new ReentrantLock; - - private val routerGenerationLock = new ReentrantLock; - public new(BundleContext bundleContext) { _brokerBundleContext = bundleContext; } def start() { log.info("Starting MD-SAL: Binding Aware Broker"); - initGenerator(); - - val executor = Executors.newCachedThreadPool; - - // Initialization of notificationBroker - log.info("Starting MD-SAL: Binding Aware Notification Broker"); - - log.info("Starting MD-SAL: Binding Aware Data Broker"); - - log.info("Starting MD-SAL: Binding Aware Data Broker"); - log.info("MD-SAL: Binding Aware Broker Started"); } - def initGenerator() { - // YANG Binding Class Loader - clsPool.appendClassPath(new LoaderClassPath(RpcService.classLoader)); - generator = new RuntimeCodeGenerator(clsPool); - } override registerConsumer(BindingAwareConsumer consumer, BundleContext bundleCtx) { val ctx = consumer.createContext(bundleCtx) @@ -139,247 +95,9 @@ class BindingAwareBrokerImpl implements BindingAwareBroker, RpcProviderRegistry, private def createContext(BindingAwareProvider provider, BundleContext providerCtx) { new OsgiProviderContext(providerCtx, this) } - - /** - * Returns a Managed Direct Proxy for supplied class - * - * Managed direct proxy is a generated proxy class conforming to the supplied interface - * which delegates all calls to the backing delegate. - * - * Proxy does not do any validation, null pointer checks or modifies data in any way, it - * is only use to avoid exposing direct references to backing implementation of service. - * - * If proxy class does not exist for supplied service class it will be generated automatically. - */ - private def getManagedDirectProxy(Class service) { - var RpcProxyContext existing = null - - if ((existing = managedProxies.get(service)) != null) { - return existing.proxy - } - return withLock(proxyGenerationLock) [ | - val maybeProxy = managedProxies.get(service); - if (maybeProxy !== null) { - return maybeProxy.proxy; - } - - - val proxyInstance = generator.getDirectProxyFor(service) - val rpcProxyCtx = new RpcProxyContext(proxyInstance.class) - val properties = new Hashtable() - rpcProxyCtx.proxy = proxyInstance as RpcService - properties.salServiceType = SAL_SERVICE_TYPE_CONSUMER_PROXY - rpcProxyCtx.registration = brokerBundleContext.registerService(service, rpcProxyCtx.proxy as T, properties) - managedProxies.put(service, rpcProxyCtx) - return rpcProxyCtx.proxy - ] - } - - private static def T withLock(ReentrantLock lock, Callable method) { - try { - lock.lock(); - val ret = method.call; - return ret; - } finally { - lock.unlock(); - } - } - - /** - * Registers RPC Implementation - * - */ - override addRpcImplementation(Class type, T service) { - checkNotNull(type, "Service type should not be null") - checkNotNull(service, "Service type should not be null") - - val proxy = getManagedDirectProxy(type) - checkState(proxy.delegate === null, "The Service for type %s is already registered", type) - - proxy.delegate = service; - return new RpcServiceRegistrationImpl(type, service, this); - } - - override RoutedRpcRegistration addRoutedRpcImplementation(Class type, T service) { - checkNotNull(type, "Service type should not be null") - checkNotNull(service, "Service type should not be null") - - val router = resolveRpcRouter(type); - checkState(router !== null) - return new RoutedRpcRegistrationImpl(service, router, this) - } - override getRpcService(Class service) { - checkNotNull(service, "Service should not be null"); - return getManagedDirectProxy(service) as T; - } - - private def RpcRouter resolveRpcRouter(Class type) { - - val router = rpcRouters.get(type); - if (router !== null) { - return router as RpcRouter; - } - - // We created Router - return withLock(routerGenerationLock) [ | - val maybeRouter = rpcRouters.get(type); - if (maybeRouter !== null) { - return maybeRouter as RpcRouter; - } - - val newRouter = generator.getRouterFor(type); - checkState(newRouter !== null); - rpcRouters.put(type, newRouter); - // We create / update Direct Proxy for router - val proxy = getManagedDirectProxy(type); - proxy.delegate = newRouter.invocationProxy - return newRouter; - ] - - } - - protected def void registerPath(RoutedRpcRegistrationImpl registration, - Class context, InstanceIdentifier path) { - - val router = registration.router; - val paths = registration.registeredPaths; - - val routingTable = router.getRoutingTable(context) - checkState(routingTable != null); - - // Updating internal structure of registration - routingTable.updateRoute(path, registration.instance) - - // Update routing table / send announce to message bus - val success = paths.put(context, path); - } - - protected def void unregisterPath(RoutedRpcRegistrationImpl registration, - Class context, InstanceIdentifier path) { - - val router = registration.router; - val paths = registration.registeredPaths; - - val routingTable = router.getRoutingTable(context) - checkState(routingTable != null); - - // Updating internal structure of registration - val target = routingTable.getRoute(path) - checkState(target === registration.instance) - routingTable.removeRoute(path) - checkState(paths.remove(context, path)); - } - - protected def void unregisterRoutedRpcService(RoutedRpcRegistrationImpl registration) { - - val router = registration.router; - val paths = registration.registeredPaths; - - for (ctxMap : registration.registeredPaths.entries) { - val context = ctxMap.key - val routingTable = router.getRoutingTable(context) - val path = ctxMap.value - routingTable.removeRoute(path) - } - } - - protected def void unregisterRpcService(RpcServiceRegistrationImpl registration) { - - val type = registration.serviceType; - - val proxy = managedProxies.get(type); - if (proxy.proxy.delegate === registration.instance) { - proxy.proxy.delegate = null; - } - } - - def createDelegate(Class type) { - getManagedDirectProxy(type); - } - - def getRpcRouters() { - return Collections.unmodifiableMap(rpcRouters); - } - - override close() { - dataConsumerRegistration.unregister() - dataProviderRegistration.unregister() - notifyConsumerRegistration.unregister() - notifyProviderRegistration.unregister() - } - -} - -class RoutedRpcRegistrationImpl extends AbstractObjectRegistration implements RoutedRpcRegistration { - - @Property - private val BindingAwareBrokerImpl broker; - - @Property - private val RpcRouter router; - - @Property - private val Multimap, InstanceIdentifier> registeredPaths = HashMultimap.create(); - - private var closed = false; - - new(T instance, RpcRouter backingRouter, BindingAwareBrokerImpl broker) { - super(instance) - _router = backingRouter; - _broker = broker; - } - - override protected removeRegistration() { - closed = true - broker.unregisterRoutedRpcService(this) - } - - override registerInstance(Class context, InstanceIdentifier instance) { - registerPath(context, instance); - } - - override unregisterInstance(Class context, InstanceIdentifier instance) { - unregisterPath(context, instance); - } - - override registerPath(Class context, InstanceIdentifier path) { - checkClosed() - broker.registerPath(this, context, path); - } - - override unregisterPath(Class context, InstanceIdentifier path) { - checkClosed() - broker.unregisterPath(this, context, path); - } - - override getServiceType() { - return router.serviceType; - } - - private def checkClosed() { - if (closed) - throw new IllegalStateException("Registration was closed."); - } - -} - -class RpcServiceRegistrationImpl extends AbstractObjectRegistration implements RpcRegistration { - - private var BindingAwareBrokerImpl broker; - - @Property - val Class serviceType; - - public new(Class type, T service, BindingAwareBrokerImpl broker) { - super(service); - this._serviceType = type; - this.broker = broker; - } - - override protected removeRegistration() { - broker.unregisterRpcService(this); - broker = null; + override close() throws Exception { + } -} +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend index e8b3850b77..b10c06f0c5 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/NotificationBrokerImpl.xtend @@ -20,8 +20,8 @@ import org.opendaylight.yangtools.concepts.AbstractObjectRegistration import org.opendaylight.yangtools.concepts.ListenerRegistration import org.opendaylight.yangtools.concepts.Registration import org.opendaylight.yangtools.yang.binding.Notification -import org.slf4j.LoggerFactory - +import org.slf4j.LoggerFactory import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder + class NotificationBrokerImpl implements NotificationProviderService, AutoCloseable { val Multimap, NotificationListener> listeners; @@ -29,6 +29,11 @@ class NotificationBrokerImpl implements NotificationProviderService, AutoCloseab @Property var ExecutorService executor; + new() { + listeners = HashMultimap.create() + } + + @Deprecated new(ExecutorService executor) { listeners = HashMultimap.create() this.executor = executor; @@ -100,7 +105,7 @@ class NotificationBrokerImpl implements NotificationProviderService, AutoCloseab override registerNotificationListener( org.opendaylight.yangtools.yang.binding.NotificationListener listener) { - val invoker = BindingAwareBrokerImpl.generator.invokerFactory.invokerFor(listener); + val invoker = SingletonHolder.INVOKER_FACTORY.invokerFor(listener); for (notifyType : invoker.supportedNotifications) { listeners.put(notifyType, invoker.invocationProxy) } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/OsgiConsumerContext.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/OsgiConsumerContext.xtend index bc53108675..644c50b86a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/OsgiConsumerContext.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/OsgiConsumerContext.xtend @@ -45,8 +45,7 @@ class OsgiConsumerContext implements ConsumerContext { val ref = services.iterator().next() as ServiceReference; return bundleContext.getService(ref) as T; } else { - broker.createDelegate(module); - return getRpcService(module); + return broker.getRpcService(module); } } catch (InvalidSyntaxException e) { log.error("Created filter was invalid:", e.message, e) diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java new file mode 100644 index 0000000000..bc862886d7 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java @@ -0,0 +1,166 @@ +package org.opendaylight.controller.sal.binding.impl; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; +import java.util.Set; +import java.util.WeakHashMap; + +import javax.swing.tree.ExpandVetoException; + +import org.opendaylight.controller.md.sal.common.api.routing.RouteChange; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher; +import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; +import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator; +import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper; +import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; +import org.opendaylight.controller.sal.binding.spi.RpcContextIdentifier; +import org.opendaylight.controller.sal.binding.spi.RpcRouter; +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.util.ListenerRegistry; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.RpcService; + +import static com.google.common.base.Preconditions.*; + +public class RpcProviderRegistryImpl implements // + RpcProviderRegistry, // + RouteChangePublisher> { + + private RuntimeCodeGenerator rpcFactory = SingletonHolder.RPC_GENERATOR_IMPL; + + private final Map, RpcService> publicProxies = new WeakHashMap<>(); + private final Map, RpcRouter> rpcRouters = new WeakHashMap<>(); + private final ListenerRegistry>> routeChangeListeners = ListenerRegistry + .create(); + + @Override + public final RoutedRpcRegistration addRoutedRpcImplementation(Class type, + T implementation) throws IllegalStateException { + return getRpcRouter(type).addRoutedRpcImplementation(implementation); + } + + @Override + public final RpcRegistration addRpcImplementation(Class type, T implementation) + throws IllegalStateException { + RpcRouter potentialRouter = (RpcRouter) rpcRouters.get(type); + if (potentialRouter != null) { + checkState(potentialRouter.getDefaultService() == null, + "Default service for routed RPC already registered."); + return potentialRouter.registerDefaultService(implementation); + } + T publicProxy = getRpcService(type); + RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy); + checkState(currentDelegate == null, "Rpc service is already registered"); + RuntimeCodeHelper.setDelegate(publicProxy, implementation); + return new RpcProxyRegistration(type, implementation, this); + } + + @Override + public final T getRpcService(Class type) { + + RpcService potentialProxy = publicProxies.get(type); + if (potentialProxy != null) { + return (T) potentialProxy; + } + T proxy = rpcFactory.getDirectProxyFor(type); + publicProxies.put(type, proxy); + return proxy; + } + + private RpcRouter getRpcRouter(Class type) { + RpcRouter potentialRouter = rpcRouters.get(type); + if (potentialRouter != null) { + return (RpcRouter) potentialRouter; + } + RpcRouter router = rpcFactory.getRouterFor(type); + router.registerRouteChangeListener(new RouteChangeForwarder(type)); + RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy()); + rpcRouters.put(type, router); + return router; + } + + public >> ListenerRegistration registerRouteChangeListener( + L listener) { + return (ListenerRegistration) routeChangeListeners.register(listener); + } + + public RuntimeCodeGenerator getRpcFactory() { + return rpcFactory; + } + + public void setRpcFactory(RuntimeCodeGenerator rpcFactory) { + this.rpcFactory = rpcFactory; + } + + private class RouteChangeForwarder implements + RouteChangeListener, InstanceIdentifier> { + + private final Class type; + + public RouteChangeForwarder(Class type) { + this.type = type; + } + + @Override + public void onRouteChange(RouteChange, InstanceIdentifier> change) { + Map>> announcements = new HashMap<>(); + for (Entry, Set>> entry : change.getAnnouncements() + .entrySet()) { + RpcContextIdentifier key = RpcContextIdentifier.contextFor(type, entry.getKey()); + announcements.put(key, entry.getValue()); + } + Map>> removals = new HashMap<>(); + for (Entry, Set>> entry : change.getRemovals() + .entrySet()) { + RpcContextIdentifier key = RpcContextIdentifier.contextFor(type, entry.getKey()); + removals.put(key, entry.getValue()); + } + RouteChange> toPublish = RoutingUtils + .> change(announcements, removals); + for (ListenerRegistration>> listener : routeChangeListeners) { + try { + listener.getInstance().onRouteChange(toPublish); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public static class RpcProxyRegistration extends AbstractObjectRegistration implements + RpcRegistration { + + private final Class serviceType; + private RpcProviderRegistryImpl registry; + + public RpcProxyRegistration(Class type, T service, RpcProviderRegistryImpl registry) { + super(service); + serviceType = type; + } + + @Override + public Class getServiceType() { + return serviceType; + } + + @Override + protected void removeRegistration() { + if (registry != null) { + T publicProxy = registry.getRpcService(serviceType); + RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy); + if (currentDelegate == getInstance()) { + RuntimeCodeHelper.setDelegate(publicProxy, null); + } + registry = null; + } + } + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentConnector.java similarity index 58% rename from opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java rename to opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentConnector.java index 9eff29f8cc..daa3914cf7 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentDataServiceConnector.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentConnector.java @@ -1,42 +1,78 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; + import org.opendaylight.controller.md.sal.common.api.RegistrationListener; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration; import org.opendaylight.controller.md.sal.common.api.data.DataModification; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChange; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.controller.sal.binding.api.data.DataProviderService; import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider; +import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl; +import org.opendaylight.controller.sal.binding.spi.RpcContextIdentifier; +import org.opendaylight.controller.sal.binding.spi.RpcRouter; import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction; +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.concepts.Registration; +import org.opendaylight.yangtools.concepts.util.ClassLoaderUtils; import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.BindingMapping; +import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.RpcInput; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.opendaylight.yangtools.yang.binding.util.BindingReflections; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BindingIndependentDataServiceConnector implements // +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; + +import static com.google.common.base.Preconditions.*; +import static org.opendaylight.yangtools.concepts.util.ClassLoaderUtils.*; + +public class BindingIndependentConnector implements // RuntimeDataProvider, // - Provider, AutoCloseable { + Provider, // + AutoCloseable { - private final Logger LOG = LoggerFactory.getLogger(BindingIndependentDataServiceConnector.class); + private final Logger LOG = LoggerFactory.getLogger(BindingIndependentConnector.class); private static final InstanceIdentifier ROOT = InstanceIdentifier.builder().toInstance(); @@ -59,26 +95,41 @@ public class BindingIndependentDataServiceConnector implements // private Registration> biCommitHandlerRegistration; + private RpcProvisionRegistry biRpcRegistry; + private RpcProviderRegistryImpl baRpcRegistry; + + private ListenerRegistration domToBindingRpcManager; + // private ListenerRegistration + // bindingToDomRpcManager; + + private Function, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier = new Function, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() { + + @Override + public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(InstanceIdentifier input) { + return mappingService.toDataDom(input); + } + + }; + @Override public DataObject readOperationalData(InstanceIdentifier path) { try { org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path); - - + CompositeNode result = biDataService.readOperationalData(biPath); Class targetType = path.getTargetType(); - - if(Augmentation.class.isAssignableFrom(targetType)) { + + if (Augmentation.class.isAssignableFrom(targetType)) { path = mappingService.fromDataDom(biPath); Class> augmentType = (Class>) targetType; DataObject parentTo = mappingService.dataObjectFromDataDom(path, result); - if(parentTo instanceof Augmentable) { + if (parentTo instanceof Augmentable) { return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType); } - + } return mappingService.dataObjectFromDataDom(path, result); - + } catch (DeserializationException e) { throw new IllegalStateException(e); } @@ -183,11 +234,24 @@ public class BindingIndependentDataServiceConnector implements // this.baDataService = baDataService; } + public RpcProviderRegistry getRpcRegistry() { + return baRpcRegistry; + } + + public void setRpcRegistry(RpcProviderRegistryImpl rpcRegistry) { + this.baRpcRegistry = rpcRegistry; + } + public void start() { baDataService.registerDataReader(ROOT, this); baCommitHandlerRegistration = baDataService.registerCommitHandler(ROOT, bindingToDomCommitHandler); biCommitHandlerRegistration = biDataService.registerCommitHandler(ROOT_BI, domToBindingCommitHandler); baDataService.registerCommitHandlerListener(domToBindingCommitHandler); + + if (baRpcRegistry != null && biRpcRegistry != null) { + domToBindingRpcManager = baRpcRegistry.registerRouteChangeListener(new DomToBindingRpcForwardingManager()); + + } } public void setMappingService(BindingIndependentMappingService mappingService) { @@ -205,6 +269,14 @@ public class BindingIndependentDataServiceConnector implements // start(); } + public void onRpcRouterCreated(Class serviceType, RpcRouter router) { + + } + + public void setDomRpcRegistry(RpcProvisionRegistry registry) { + biRpcRegistry = registry; + } + @Override public void close() throws Exception { if (baCommitHandlerRegistration != null) { @@ -358,4 +430,204 @@ public class BindingIndependentDataServiceConnector implements // return forwardedTransaction; } } + + private class DomToBindingRpcForwardingManager implements + RouteChangeListener> { + + private final Map, DomToBindingRpcForwarder> forwarders = new WeakHashMap<>(); + + @Override + public void onRouteChange(RouteChange> change) { + for (Entry>> entry : change.getAnnouncements().entrySet()) { + bindingRoutesAdded(entry); + } + } + + private void bindingRoutesAdded(Entry>> entry) { + Class context = entry.getKey().getRoutingContext(); + Class service = entry.getKey().getRpcService(); + if (context != null) { + getRpcForwarder(service, context).registerPaths(context, service, entry.getValue()); + } + } + + private DomToBindingRpcForwarder getRpcForwarder(Class service, + Class context) { + DomToBindingRpcForwarder potential = forwarders.get(service); + if (potential != null) { + return potential; + } + if (context == null) { + potential = new DomToBindingRpcForwarder(service); + } else { + potential = new DomToBindingRpcForwarder(service, context); + } + forwarders.put(service, potential); + return potential; + } + + } + + private class DomToBindingRpcForwarder implements RpcImplementation { + + private final Set supportedRpcs; + private final WeakReference> rpcServiceType; + private Set registrations; + + public DomToBindingRpcForwarder(Class service) { + this.rpcServiceType = new WeakReference>(service); + this.supportedRpcs = mappingService.getRpcQNamesFor(service); + for (QName rpc : supportedRpcs) { + biRpcRegistry.addRpcImplementation(rpc, this); + } + registrations = ImmutableSet.of(); + } + + public DomToBindingRpcForwarder(Class service, Class context) { + this.rpcServiceType = new WeakReference>(service); + this.supportedRpcs = mappingService.getRpcQNamesFor(service); + registrations = new HashSet<>(); + for (QName rpc : supportedRpcs) { + registrations.add(biRpcRegistry.addRoutedRpcImplementation(rpc, this)); + } + registrations = ImmutableSet.copyOf(registrations); + } + + public void registerPaths(Class context, Class service, + Set> set) { + QName ctx = BindingReflections.findQName(context); + for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform( + toDOMInstanceIdentifier)) { + for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) { + reg.registerPath(ctx, path); + } + } + } + + public void removePaths(Class context, Class service, + Set> set) { + QName ctx = BindingReflections.findQName(context); + for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform( + toDOMInstanceIdentifier)) { + for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) { + reg.unregisterPath(ctx, path); + } + } + } + + @Override + public Set getSupportedRpcs() { + return supportedRpcs; + } + + @Override + public RpcResult invokeRpc(QName rpc, CompositeNode domInput) { + checkArgument(rpc != null); + checkArgument(domInput != null); + + Class rpcType = rpcServiceType.get(); + checkState(rpcType != null); + RpcService rpcService = baRpcRegistry.getRpcService(rpcType); + checkState(rpcService != null); + CompositeNode domUnwrappedInput = domInput.getFirstCompositeByName(QName.create(rpc, "input")); + try { + return resolveInvocationStrategy(rpc, rpcType).invokeOn(rpcService, domUnwrappedInput); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc, + final Class rpcType) throws Exception { + return ClassLoaderUtils.withClassLoader(rpcType.getClassLoader(), new Callable() { + @Override + public RpcInvocationStrategy call() throws Exception { + String methodName = BindingMapping.getMethodName(rpc); + Method targetMethod = null; + for (Method possibleMethod : rpcType.getMethods()) { + if (possibleMethod.getName().equals(methodName) + && BindingReflections.isRpcMethod(possibleMethod)) { + targetMethod = possibleMethod; + break; + } + } + checkState(targetMethod != null, "Rpc method not found"); + Optional> outputClass = BindingReflections.resolveRpcOutputClass(targetMethod); + Optional> inputClass = BindingReflections + .resolveRpcInputClass(targetMethod); + + RpcInvocationStrategy strategy = null; + if (outputClass.isPresent()) { + if (inputClass.isPresent()) { + strategy = new DefaultInvocationStrategy(targetMethod, outputClass.get(), inputClass.get()); + } else { + strategy = new NoInputNoOutputInvocationStrategy(targetMethod); + } + } else { + strategy = null; + } + return strategy; + } + + }); + } + } + + private abstract class RpcInvocationStrategy { + + protected final Method targetMethod; + + public RpcInvocationStrategy(Method targetMethod) { + this.targetMethod = targetMethod; + } + + public abstract RpcResult uncheckedInvoke(RpcService rpcService, CompositeNode domInput) + throws Exception; + + public RpcResult invokeOn(RpcService rpcService, CompositeNode domInput) throws Exception { + return uncheckedInvoke(rpcService, domInput); + } + } + + private class DefaultInvocationStrategy extends RpcInvocationStrategy { + + @SuppressWarnings("rawtypes") + private WeakReference inputClass; + + @SuppressWarnings("rawtypes") + private WeakReference outputClass; + + public DefaultInvocationStrategy(Method targetMethod, Class outputClass, + Class inputClass) { + super(targetMethod); + this.outputClass = new WeakReference(outputClass); + this.inputClass = new WeakReference(inputClass); + } + + @Override + public RpcResult uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception { + DataContainer bindingInput = mappingService.dataObjectFromDataDom(inputClass.get(), domInput); + Future> result = (Future>) targetMethod.invoke(rpcService, bindingInput); + if (result == null) { + return Rpcs.getRpcResult(false); + } + RpcResult bindingResult = result.get(); + return Rpcs.getRpcResult(true); + } + + } + + private class NoInputNoOutputInvocationStrategy extends RpcInvocationStrategy { + + public NoInputNoOutputInvocationStrategy(Method targetMethod) { + super(targetMethod); + } + + public RpcResult uncheckedInvoke(RpcService rpcService, CompositeNode domInput) throws Exception { + Future> result = (Future>) targetMethod.invoke(rpcService); + RpcResult bindingResult = result.get(); + return Rpcs.getRpcResult(bindingResult.isSuccessful(), bindingResult.getErrors()); + } + + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentMappingService.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentMappingService.java index b1983fe224..e16ae48ef4 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentMappingService.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentMappingService.java @@ -1,10 +1,13 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; import java.util.Map.Entry; +import java.util.Set; -import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.RpcService; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; public interface BindingIndependentMappingService { @@ -19,5 +22,9 @@ public interface BindingIndependentMappingService { DataObject dataObjectFromDataDom(InstanceIdentifier path, CompositeNode result) throws DeserializationException; InstanceIdentifier fromDataDom(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry) throws DeserializationException; + + Set getRpcQNamesFor(Class service); + + DataContainer dataObjectFromDataDom(Class inputClass, CompositeNode domInput); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RoutingContext.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RoutingContext.java new file mode 100644 index 0000000000..49e056b100 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RoutingContext.java @@ -0,0 +1,5 @@ +package org.opendaylight.controller.sal.binding.spi; + +public class RoutingContext { + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcContextIdentifier.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcContextIdentifier.java new file mode 100644 index 0000000000..33569eb077 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcContextIdentifier.java @@ -0,0 +1,65 @@ +package org.opendaylight.controller.sal.binding.spi; + +import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.RpcService; + +public final class RpcContextIdentifier implements Immutable{ + + public final Class rpcService; + public final Class routingContext; + + private RpcContextIdentifier(Class rpcService, Class routingContext) { + super(); + this.rpcService = rpcService; + this.routingContext = routingContext; + } + + public Class getRpcService() { + return rpcService; + } + + public Class getRoutingContext() { + return routingContext; + } + + public static final RpcContextIdentifier contextForGlobalRpc(Class serviceType) { + return new RpcContextIdentifier(serviceType, null); + } + + public static final RpcContextIdentifier contextFor(Class serviceType,Class routingContext) { + return new RpcContextIdentifier(serviceType, routingContext); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((routingContext == null) ? 0 : routingContext.hashCode()); + result = prime * result + ((rpcService == null) ? 0 : rpcService.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RpcContextIdentifier other = (RpcContextIdentifier) obj; + if (routingContext == null) { + if (other.routingContext != null) + return false; + } else if (!routingContext.equals(other.routingContext)) + return false; + if (rpcService == null) { + if (other.rpcService != null) + return false; + } else if (!rpcService.equals(other.rpcService)) + return false; + return true; + } + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcRouter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcRouter.java index 7db90b62fd..621d048dfd 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcRouter.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/spi/RpcRouter.java @@ -9,6 +9,9 @@ package org.opendaylight.controller.sal.binding.spi; import java.util.Set; +import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; import org.opendaylight.yangtools.yang.binding.BaseIdentity; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.RpcImplementation; @@ -26,7 +29,8 @@ import org.opendaylight.yangtools.yang.binding.RpcService; * Type of RpcService for which router provides routing information * and route selection. */ -public interface RpcRouter extends RpcImplementation{ +public interface RpcRouter extends // + RouteChangePublisher, InstanceIdentifier> { /** * Returns a type of RpcService which is served by this instance of router. @@ -72,12 +76,11 @@ public interface RpcRouter extends RpcImplementation{ * @return default instance responsible for processing RPCs. */ T getDefaultService(); - - /** - * - */ - void setDefaultService(T service); Set> getContexts(); + + RoutedRpcRegistration addRoutedRpcImplementation(T service); + + RpcRegistration registerDefaultService(T service); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java index a7a70c2839..633506fec6 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/AbstractDataServiceTest.java @@ -12,7 +12,7 @@ import org.junit.After; import org.junit.Before; import org.opendaylight.controller.sal.binding.api.data.DataProviderService; import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector; +import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService; import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory; import org.opendaylight.controller.sal.binding.test.util.BindingTestContext; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/RuntimeCodeGeneratorTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/RuntimeCodeGeneratorTest.java index 1a97bd693a..9c9841a4a5 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/RuntimeCodeGeneratorTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/RuntimeCodeGeneratorTest.java @@ -65,9 +65,6 @@ public class RuntimeCodeGeneratorTest { assertNotNull(product); assertNotNull(product.getInvocationProxy()); - assertNotNull(product.getSupportedInputs()); - assertTrue(product.getSupportedInputs().contains(SimpleInput.class)); - assertTrue(product.getSupportedInputs().contains(InheritedContextInput.class)); assertEquals("2 fields should be generated.", 2, product.getInvocationProxy().getClass().getFields().length); verifyRouting(product); diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java index 4e611c5fe2..3217a31329 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java @@ -7,15 +7,22 @@ import java.util.Set; import javassist.ClassPool; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.controller.sal.binding.api.data.DataProviderService; import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl; +import org.opendaylight.controller.sal.binding.impl.BindingAwareBrokerImpl; import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector; +import org.opendaylight.controller.sal.binding.impl.NotificationBrokerImpl; +import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService; import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.controller.sal.core.api.data.DataStore; +import org.opendaylight.controller.sal.dom.broker.BrokerImpl; import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper; import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore; +import org.opendaylight.controller.sal.dom.broker.impl.RpcRouterImpl; import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -31,7 +38,7 @@ import com.google.common.util.concurrent.MoreExecutors; import static com.google.common.base.Preconditions.*; -public class BindingTestContext { +public class BindingTestContext implements AutoCloseable { public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier @@ -40,10 +47,15 @@ public class BindingTestContext { private static final Logger LOG = LoggerFactory.getLogger(BindingTestContext.class); private RuntimeGeneratedMappingServiceImpl mappingServiceImpl; + + + private BindingAwareBrokerImpl baBrokerImpl; private DataBrokerImpl baDataImpl; + private NotificationBrokerImpl baNotifyImpl; + private BindingIndependentConnector baConnectDataServiceImpl; + private org.opendaylight.controller.sal.dom.broker.DataBrokerImpl biDataImpl; - - private BindingIndependentDataServiceConnector connectorServiceImpl; + private BrokerImpl biBrokerImpl; private HashMapDataStore rawDataStore; private SchemaAwareDataStoreAdapter schemaAwareDataStore; private DataStoreStatsWrapper dataStoreStats; @@ -56,6 +68,7 @@ public class BindingTestContext { private final ClassPool classPool; private final boolean startWithSchema; + protected BindingTestContext(ListeningExecutorService executor, ClassPool classPool, boolean startWithSchema) { this.executor = executor; @@ -93,15 +106,29 @@ public class BindingTestContext { baDataImpl.setExecutor(executor); } + public void startBindingBroker() { + checkState(executor != null,"Executor needs to be set"); + checkState(baDataImpl != null,"Binding Data Broker must be started"); + checkState(baNotifyImpl != null, "Notification Service must be started"); + baBrokerImpl = new BindingAwareBrokerImpl(null); + + baBrokerImpl.setDataBroker(baDataImpl); + baBrokerImpl.setNotifyBroker(baNotifyImpl); + + baBrokerImpl.start(); + } + public void startBindingToDomDataConnector() { checkState(baDataImpl != null,"Binding Data Broker needs to be started"); checkState(biDataImpl != null,"DOM Data Broker needs to be started."); checkState(mappingServiceImpl != null,"DOM Mapping Service needs to be started."); - connectorServiceImpl = new BindingIndependentDataServiceConnector(); - connectorServiceImpl.setBaDataService(baDataImpl); - connectorServiceImpl.setBiDataService(biDataImpl); - connectorServiceImpl.setMappingService(mappingServiceImpl); - connectorServiceImpl.start(); + baConnectDataServiceImpl = new BindingIndependentConnector(); + baConnectDataServiceImpl.setRpcRegistry(baBrokerImpl); + baConnectDataServiceImpl.setDomRpcRegistry(getDomRpcRegistry()); + baConnectDataServiceImpl.setBaDataService(baDataImpl); + baConnectDataServiceImpl.setBiDataService(biDataImpl); + baConnectDataServiceImpl.setMappingService(mappingServiceImpl); + baConnectDataServiceImpl.start(); } public void startBindingToDomMappingService() { @@ -149,8 +176,11 @@ public class BindingTestContext { public void start() { startBindingDataBroker(); + startBindingNotificationBroker(); + startBindingBroker(); startDomDataBroker(); startDomDataStore(); + startDomBroker(); startBindingToDomMappingService(); startBindingToDomDataConnector(); if(startWithSchema) { @@ -158,6 +188,19 @@ public class BindingTestContext { } } + private void startDomBroker() { + checkState(executor != null); + biBrokerImpl = new BrokerImpl(); + biBrokerImpl.setExecutor(executor); + biBrokerImpl.setRouter(new RpcRouterImpl("test")); + } + + public void startBindingNotificationBroker() { + checkState(executor != null); + baNotifyImpl = new NotificationBrokerImpl(executor); + + } + public void loadYangSchemaFromClasspath() { String[] files = getAllYangFilesOnClasspath(); updateYangSchema(files); @@ -196,4 +239,24 @@ public class BindingTestContext { dataStoreStats.getRequestCommitCount(), dataStoreStats.getRequestCommitTotalTime(), dataStoreStats.getRequestCommitAverageTime()); } + + public RpcProviderRegistry getBindingRpcRegistry() { + return baBrokerImpl; + } + + public RpcProvisionRegistry getDomRpcRegistry() { + if(biBrokerImpl == null) { + return null; + } + return biBrokerImpl.getRouter(); + } + + public RpcImplementation getDomRpcInvoker() { + return biBrokerImpl.getRouter(); + } + + @Override + public void close() throws Exception { + + } } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java new file mode 100644 index 0000000000..92a0a3a98d --- /dev/null +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/CrossBrokerRpcTest.java @@ -0,0 +1,159 @@ +package org.opendaylight.controller.sal.binding.test.connect.dom; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.concurrent.Future; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory; +import org.opendaylight.controller.sal.binding.test.util.BindingTestContext; +import org.opendaylight.controller.sal.common.util.Rpcs; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlowRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; + +import static junit.framework.Assert.*; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; + +public class CrossBrokerRpcTest { + + protected RpcProviderRegistry baRpcRegistry; + protected RpcProvisionRegistry biRpcRegistry; + private BindingTestContext testContext; + private RpcImplementation biRpcInvoker; + private MessageCapturingFlowService flowService; + + public static final NodeId NODE_A = new NodeId("a"); + public static final NodeId NODE_B = new NodeId("b"); + public static final NodeId NODE_C = new NodeId("c"); + public static final NodeId NODE_D = new NodeId("d"); + + public static final InstanceIdentifier BA_NODE_A_ID = createBANodeIdentifier(NODE_A); + public static final InstanceIdentifier BA_NODE_B_ID = createBANodeIdentifier(NODE_B); + public static final InstanceIdentifier BA_NODE_C_ID = createBANodeIdentifier(NODE_C); + public static final InstanceIdentifier BA_NODE_D_ID = createBANodeIdentifier(NODE_D); + + public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_A_ID = createBINodeIdentifier(NODE_A); + public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_B_ID = createBINodeIdentifier(NODE_B); + public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_C_ID = createBINodeIdentifier(NODE_C); + public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier BI_NODE_D_ID = createBINodeIdentifier(NODE_D); + + private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id"); + private static final QName ADD_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "add-flow"); + private static final QName REMOVE_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "remove-flow"); + private static final QName UPDATE_FLOW_QNAME = QName.create(NodeFlowRemoved.QNAME, "update-flow"); + + @Before + public void setup() { + BindingBrokerTestFactory testFactory = new BindingBrokerTestFactory(); + testFactory.setExecutor(MoreExecutors.sameThreadExecutor()); + testFactory.setStartWithParsedSchema(true); + testContext = testFactory.getTestContext(); + + testContext.start(); + baRpcRegistry = testContext.getBindingRpcRegistry(); + biRpcRegistry = testContext.getDomRpcRegistry(); + biRpcInvoker = testContext.getDomRpcInvoker(); + assertNotNull(baRpcRegistry); + assertNotNull(biRpcRegistry); + + flowService = MessageCapturingFlowService.create(baRpcRegistry); + + } + + @Test + public void bindingRoutedRpcProvider_DomInvokerTest() { + + flowService// + .registerPath(NodeContext.class, BA_NODE_A_ID) // + .registerPath(NodeContext.class, BA_NODE_B_ID) // + .setAddFlowResult(addFlowResult(true, 10)); + + SalFlowService baFlowInvoker = baRpcRegistry.getRpcService(SalFlowService.class); + assertNotSame(flowService, baFlowInvoker); + + AddFlowInput addFlowA = addFlow(BA_NODE_A_ID) // + .setPriority(100).setBarrier(true).build(); + + CompositeNode addFlowDom = toDomRpc(ADD_FLOW_QNAME, addFlowA); + assertNotNull(addFlowDom); + RpcResult domResult = biRpcInvoker.invokeRpc(ADD_FLOW_QNAME, addFlowDom); + assertNotNull(domResult); + assertTrue("DOM result is successful.", domResult.isSuccessful()); + assertTrue("Bidning Add Flow RPC was captured.", flowService.getReceivedAddFlows().containsKey(BA_NODE_A_ID)); + assertEquals(addFlowA, flowService.getReceivedAddFlows().get(BA_NODE_A_ID).iterator().next()); + } + + public void bindingRpcInvoker_DomRoutedProviderTest() { + + } + + private CompositeNode toDomRpcInput(DataObject addFlowA) { + return testContext.getBindingToDomMappingService().toDataDom(addFlowA); + } + + @After + public void teardown() throws Exception { + testContext.close(); + } + + private static InstanceIdentifier createBANodeIdentifier(NodeId node) { + return InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(node)).toInstance(); + } + + private static org.opendaylight.yangtools.yang.data.api.InstanceIdentifier createBINodeIdentifier(NodeId node) { + return org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder().node(Nodes.QNAME) + .nodeWithKey(Node.QNAME, NODE_ID_QNAME, node.getValue()).toInstance(); + } + + private Future> addFlowResult(boolean success, long xid) { + AddFlowOutput output = new AddFlowOutputBuilder() // + .setTransactionId(new TransactionId(BigInteger.valueOf(xid))).build(); + RpcResult result = Rpcs.getRpcResult(success, output, ImmutableList. of()); + return Futures.immediateFuture(result); + } + + private static AddFlowInputBuilder addFlow(InstanceIdentifier nodeId) { + AddFlowInputBuilder builder = new AddFlowInputBuilder(); + builder.setNode(new NodeRef(nodeId)); + return builder; + } + + private CompositeNode toDomRpc(QName rpcName, AddFlowInput addFlowA) { + return new CompositeNodeTOImpl(rpcName, null, + Collections.> singletonList(toDomRpcInput(addFlowA))); + } +} diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java new file mode 100644 index 0000000000..74f07818d5 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/MessageCapturingFlowService.java @@ -0,0 +1,122 @@ +package org.opendaylight.controller.sal.binding.test.connect.dom; + +import static junit.framework.Assert.assertNotNull; + +import java.util.concurrent.Future; + +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput; +import org.opendaylight.yangtools.yang.binding.BaseIdentity; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +public class MessageCapturingFlowService implements SalFlowService, AutoCloseable { + + private Future> addFlowResult; + private Future> removeFlowResult; + private Future> updateFlowResult; + + private final Multimap, AddFlowInput> receivedAddFlows = HashMultimap.create(); + private final Multimap, RemoveFlowInput> receivedRemoveFlows = HashMultimap.create(); + private final Multimap, UpdateFlowInput> receivedUpdateFlows = HashMultimap.create(); + private RoutedRpcRegistration registration; + + @Override + public Future> addFlow(AddFlowInput arg0) { + receivedAddFlows.put(arg0.getNode().getValue(), arg0); + return addFlowResult; + } + + @Override + public Future> removeFlow(RemoveFlowInput arg0) { + receivedRemoveFlows.put(arg0.getNode().getValue(), arg0); + return removeFlowResult; + } + + @Override + public Future> updateFlow(UpdateFlowInput arg0) { + receivedUpdateFlows.put(arg0.getNode().getValue(), arg0); + return updateFlowResult; + } + + public Future> getAddFlowResult() { + return addFlowResult; + } + + public MessageCapturingFlowService setAddFlowResult(Future> addFlowResult) { + this.addFlowResult = addFlowResult; + return this; + } + + public Future> getRemoveFlowResult() { + return removeFlowResult; + } + + public MessageCapturingFlowService setRemoveFlowResult(Future> removeFlowResult) { + this.removeFlowResult = removeFlowResult; + return this; + } + + public Future> getUpdateFlowResult() { + return updateFlowResult; + } + + public MessageCapturingFlowService setUpdateFlowResult(Future> updateFlowResult) { + this.updateFlowResult = updateFlowResult; + return this; + } + + public Multimap, AddFlowInput> getReceivedAddFlows() { + return receivedAddFlows; + } + + public Multimap, RemoveFlowInput> getReceivedRemoveFlows() { + return receivedRemoveFlows; + } + + public Multimap, UpdateFlowInput> getReceivedUpdateFlows() { + return receivedUpdateFlows; + } + + public MessageCapturingFlowService registerTo(RpcProviderRegistry registry) { + registration = registry.addRoutedRpcImplementation(SalFlowService.class, this); + assertNotNull(registration); + return this; + } + + public void close() throws Exception { + registration.close(); + } + + public MessageCapturingFlowService registerPath(Class context, InstanceIdentifier path) { + registration.registerPath(context, path); + return this; + } + + public MessageCapturingFlowService unregisterPath(Class context, InstanceIdentifier path) { + registration.unregisterPath(context, path); + return this; + } + + public static MessageCapturingFlowService create() { + return new MessageCapturingFlowService(); + } + + public static MessageCapturingFlowService create(RpcProviderRegistry registry) { + MessageCapturingFlowService ret = new MessageCapturingFlowService(); + ret.registerTo(registry); + return ret; + } + + +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 15616215d3..112b57cd33 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -54,6 +54,7 @@ public class TestHelper { mavenBundle(CONTROLLER, "config-persister-api").versionAsInProject(), // mavenBundle(CONTROLLER, "netconf-api").versionAsInProject(), // mavenBundle(CONTROLLER, "ietf-netconf-monitoring").versionAsInProject(), // + mavenBundle(CONTROLLER, "ietf-netconf-monitoring-extension").versionAsInProject(), // mavenBundle(CONTROLLER, "netconf-monitoring").versionAsInProject(), // mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), // diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java index 89851c9393..bee29a1ad1 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/routing/RouteChangePublisher.java @@ -4,5 +4,5 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration; public interface RouteChangePublisher { - ListenerRegistration> registerRouteChangeListener(RouteChangeListener listener); + > ListenerRegistration registerRouteChangeListener(L listener); } diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/RoutingUtils.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/RoutingUtils.java new file mode 100644 index 0000000000..60d0cdf766 --- /dev/null +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/routing/RoutingUtils.java @@ -0,0 +1,92 @@ +package org.opendaylight.controller.md.sal.common.impl.routing; + +import java.util.Map; +import java.util.Set; + +import org.opendaylight.controller.md.sal.common.api.routing.RouteChange; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class RoutingUtils { + + public static RouteChange removalChange(C context,P path) { + final ImmutableMap> announcements = ImmutableMap.>of(); + final ImmutableMap> removals = ImmutableMap.>of(context, ImmutableSet.of(path)); + return new RouteChangeImpl(announcements, removals); + } + + public static RouteChange announcementChange(C context,P path) { + final ImmutableMap> announcements = ImmutableMap.>of(context, ImmutableSet.of(path)); + final ImmutableMap> removals = ImmutableMap.>of(); + return new RouteChangeImpl(announcements, removals); + } + + + public static RouteChange change(Map> announcements, + Map> removals) { + final ImmutableMap> immutableAnnouncements = ImmutableMap.>copyOf(announcements); + final ImmutableMap> immutableRemovals = ImmutableMap.>copyOf(removals); + return new RouteChangeImpl(immutableAnnouncements, immutableRemovals); + } + + + private static class RouteChangeImpl implements RouteChange { + private final Map> removal; + private final Map> announcement; + + public RouteChangeImpl(ImmutableMap> removal, ImmutableMap> announcement) { + super(); + this.removal = removal; + this.announcement = announcement; + } + + @Override + public Map> getAnnouncements() { + return announcement; + } + + @Override + public Map> getRemovals() { + return removal; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((announcement == null) ? 0 : announcement.hashCode()); + result = prime * result + ((removal == null) ? 0 : removal.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + RouteChangeImpl other = (RouteChangeImpl) obj; + if (announcement == null) { + if (other.announcement != null) + return false; + } else if (!announcement.equals(other.announcement)) + return false; + if (removal == null) { + if (other.removal != null) { + return false; + } + } else if (!removal.equals(other.removal)) + return false; + return true; + } + } + + + +} diff --git a/opendaylight/md-sal/sal-common-util/pom.xml b/opendaylight/md-sal/sal-common-util/pom.xml index 7e8069a9b3..ff15e72ba6 100644 --- a/opendaylight/md-sal/sal-common-util/pom.xml +++ b/opendaylight/md-sal/sal-common-util/pom.xml @@ -23,6 +23,10 @@ concepts 0.1.1-SNAPSHOT + + com.google.guava + guava + bundle diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java index 54e1a065f4..356ec8ff7c 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Rpcs.java @@ -11,17 +11,31 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; + +import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import com.google.common.collect.ImmutableList; + public class Rpcs { + + public static RpcResult getRpcResult(boolean successful) { + RpcResult ret = new RpcResultTO(successful, null, ImmutableList.of()); + return ret; + } + public static RpcResult getRpcResult(boolean successful, T result, Collection errors) { RpcResult ret = new RpcResultTO(successful, result, errors); return ret; } - private static class RpcResultTO implements RpcResult, Serializable { + public static RpcResult getRpcResult(boolean successful, Collection errors) { + return new RpcResultTO(successful, null, errors); + } + + private static class RpcResultTO implements RpcResult, Serializable, Immutable { private final Collection errors; private final T result; @@ -31,8 +45,7 @@ public class Rpcs { Collection errors) { this.successful = successful; this.result = result; - this.errors = Collections.unmodifiableList(new ArrayList( - errors)); + this.errors = ImmutableList.copyOf(errors); } @Override diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend index 7ef594bad9..8f179987b9 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend @@ -29,8 +29,10 @@ import org.slf4j.LoggerFactory import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter import org.opendaylight.yangtools.concepts.ListenerRegistration import org.opendaylight.controller.sal.core.api.RpcRegistrationListener +import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry +import org.opendaylight.controller.sal.core.api.RpcImplementation -public class BrokerImpl implements Broker, AutoCloseable { +public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable { private static val log = LoggerFactory.getLogger(BrokerImpl); // Broker Generic Context @@ -115,4 +117,12 @@ public class BrokerImpl implements Broker, AutoCloseable { deactivator?.close(); } + override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException { + router.addRpcImplementation(rpcType,implementation); + } + + override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) { + router.addRoutedRpcImplementation(rpcType,implementation); + } + } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend index d8680ce3b4..5ee19a0e8f 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RpcRouterImpl.xtend @@ -15,6 +15,8 @@ import org.opendaylight.yangtools.concepts.AbstractObjectRegistration import org.opendaylight.controller.sal.core.api.RpcRegistrationListener import org.slf4j.LoggerFactory import org.opendaylight.yangtools.concepts.util.ListenerRegistry +import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier class RpcRouterImpl implements RpcRouter, Identifiable { @@ -35,6 +37,20 @@ class RpcRouterImpl implements RpcRouter, Identifiable { } override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) { + checkNotNull(rpcType, "Rpc Type should not be null"); + checkNotNull(implementation, "Implementation should not be null."); + val reg = new RoutedRpcRegistrationImpl(rpcType, implementation, this); + implementations.put(rpcType, reg) + + for (listener : rpcRegistrationListeners.listeners) { + try { + listener.instance.onRpcImplementationAdded(rpcType); + } catch (Exception e) { + log.error("Unhandled exception during invoking listener", e); + } + } + + return reg; } override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException { @@ -102,5 +118,23 @@ class RpcRegistrationImpl extends AbstractObjectRegistration override protected removeRegistration() { router.remove(this); } +} +class RoutedRpcRegistrationImpl extends RpcRegistrationImpl implements RoutedRpcRegistration { + + + new(QName type, RpcImplementation instance, RpcRouterImpl router) { + super(type,instance,router) + } + override protected removeRegistration() { + router.remove(this); + } + override registerPath(QName context, InstanceIdentifier path) { + // + + } + + override unregisterPath(QName context, InstanceIdentifier path) { + // + } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareDataStoreAdapter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareDataStoreAdapter.java index 4f4fadcc44..75e96491b6 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareDataStoreAdapter.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareDataStoreAdapter.java @@ -151,19 +151,20 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator original) { // NOOP for now NormalizedDataModification normalized = new NormalizedDataModification(original); - for (Entry entry : original.getUpdatedConfigurationData().entrySet()) { + for (Entry entry : original.getUpdatedConfigurationData().entrySet()) { normalized.putConfigurationData(entry.getKey(), entry.getValue()); } - for (Entry entry : original.getUpdatedOperationalData().entrySet()) { + for (Entry entry : original.getUpdatedOperationalData().entrySet()) { normalized.putOperationalData(entry.getKey(), entry.getValue()); } for (InstanceIdentifier entry : original.getRemovedConfigurationData()) { normalized.removeConfigurationData(entry); } - for(InstanceIdentifier entry : original.getRemovedOperationalData()) { + for (InstanceIdentifier entry : original.getRemovedOperationalData()) { normalized.removeOperationalData(entry); } return normalized; @@ -284,7 +285,7 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator { private Object identifier; @@ -295,12 +296,12 @@ public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator> commit() { throw new UnsupportedOperationException("Commit should not be invoked on this"); } - + @Override - protected CompositeNode mergeConfigurationData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) { - return mergeData(path,stored, modified,true); + protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored, + CompositeNode modified) { + return mergeData(path, stored, modified, true); } - + @Override - protected CompositeNode mergeOperationalData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) { + protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored, + CompositeNode modified) { // TODO Auto-generated method stub - return mergeData(path,stored,modified,false); + return mergeData(path, stored, modified, false); } - } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java index ed8f02bf56..7cd43bd79a 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java @@ -8,12 +8,16 @@ package org.opendaylight.controller.netconf.confignetconfconnector.osgi; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; +import com.google.common.base.Preconditions; import com.google.common.collect.Sets; +import org.opendaylight.controller.config.api.LookupRegistry; import org.opendaylight.controller.config.util.ConfigRegistryJMXClient; import org.opendaylight.controller.config.yang.store.api.YangStoreException; import org.opendaylight.controller.config.yang.store.api.YangStoreService; import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; import org.opendaylight.controller.netconf.mapping.api.Capability; @@ -42,12 +46,38 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { String netconfSessionIdForReporting) throws YangStoreException { yangStoreSnapshot = yangStoreService.getYangStoreSnapshot(); + checkConsistencyBetweenYangStoreAndConfig(jmxClient, yangStoreSnapshot); + transactionProvider = new TransactionProvider(jmxClient, netconfSessionIdForReporting); operationProvider = new NetconfOperationProvider(yangStoreSnapshot, jmxClient, transactionProvider, netconfSessionIdForReporting); capabilities = setupCapabilities(yangStoreSnapshot); } + + @VisibleForTesting + static void checkConsistencyBetweenYangStoreAndConfig(LookupRegistry jmxClient, YangStoreSnapshot yangStoreSnapshot) { + Set missingModulesFromConfig = Sets.newHashSet(); + + Set modulesSeenByConfig = jmxClient.getAvailableModuleFactoryQNames(); + Map> moduleMXBeanEntryMap = yangStoreSnapshot.getModuleMXBeanEntryMap(); + + for (Map moduleNameToMBE : moduleMXBeanEntryMap.values()) { + for (ModuleMXBeanEntry moduleMXBeanEntry : moduleNameToMBE.values()) { + String moduleSeenByYangStore = moduleMXBeanEntry.getYangModuleQName().toString(); + if(modulesSeenByConfig.contains(moduleSeenByYangStore) == false) + missingModulesFromConfig.add(moduleSeenByYangStore); + } + } + + Preconditions + .checkState( + missingModulesFromConfig.isEmpty(), + "There are inconsistencies between configuration subsystem and yangstore in terms of discovered yang modules, yang modules missing from config subsystem but present in yangstore: %s, %sAll modules present in config: %s", + missingModulesFromConfig, System.lineSeparator(), modulesSeenByConfig); + + } + @Override public void close() { yangStoreSnapshot.close(); diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java new file mode 100644 index 0000000000..0d459cfb86 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013 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.netconf.confignetconfconnector.osgi; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.junit.Assert; +import org.junit.Test; +import org.junit.matchers.JUnitMatchers; +import org.opendaylight.controller.config.api.LookupRegistry; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.yangtools.yang.common.QName; + +import java.net.URI; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class NetconfOperationServiceImplTest { + + private Date date = new Date(0); + + @Test + public void testCheckConsistencyBetweenYangStoreAndConfig_ok() throws Exception { + NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig( + mockJmxClient("qname1", "qname2"), + mockYangStoreSnapshot("qname2", "qname1")); + } + + @Test + public void testCheckConsistencyBetweenYangStoreAndConfig_ok2() throws Exception { + NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig( + mockJmxClient("qname1", "qname2", "qname4", "qname5"), + mockYangStoreSnapshot("qname2", "qname1")); + } + + @Test + public void testCheckConsistencyBetweenYangStoreAndConfig_ok3() throws Exception { + NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig( + mockJmxClient(), + mockYangStoreSnapshot()); + } + + @Test(expected = IllegalStateException.class) + public void testCheckConsistencyBetweenYangStoreAndConfig_yangStoreMore() throws Exception { + try { + NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig(mockJmxClient("qname1"), + mockYangStoreSnapshot("qname2", "qname1")); + } catch (IllegalStateException e) { + String message = e.getMessage(); + Assert.assertThat( + message, + JUnitMatchers + .containsString(" missing from config subsystem but present in yangstore: [(namespace?revision=1970-01-01)qname2]")); + Assert.assertThat( + message, + JUnitMatchers + .containsString("All modules present in config: [(namespace?revision=1970-01-01)qname1]")); + throw e; + } + } + + private YangStoreSnapshot mockYangStoreSnapshot(String... qnames) { + YangStoreSnapshot mock = mock(YangStoreSnapshot.class); + + Map> map = Maps.newHashMap(); + + Map innerMap = Maps.newHashMap(); + + int i = 1; + for (String qname : qnames) { + innerMap.put(Integer.toString(i++), mockMBeanEntry(qname)); + } + + map.put("1", innerMap); + + doReturn(map).when(mock).getModuleMXBeanEntryMap(); + + return mock; + } + + private ModuleMXBeanEntry mockMBeanEntry(String qname) { + ModuleMXBeanEntry mock = mock(ModuleMXBeanEntry.class); + QName q = getQName(qname); + doReturn(q).when(mock).getYangModuleQName(); + return mock; + } + + private QName getQName(String qname) { + return new QName(URI.create("namespace"), date, qname); + } + + private LookupRegistry mockJmxClient(String... visibleQNames) { + LookupRegistry mock = mock(LookupRegistry.class); + Set qnames = Sets.newHashSet(); + for (String visibleQName : visibleQNames) { + QName q = getQName(visibleQName); + qnames.add(q.toString()); + } + doReturn(qnames).when(mock).getAvailableModuleFactoryQNames(); + return mock; + } +} diff --git a/opendaylight/netconf/config-persister-impl/pom.xml b/opendaylight/netconf/config-persister-impl/pom.xml index 7fc2b584b8..20bd386063 100644 --- a/opendaylight/netconf/config-persister-impl/pom.xml +++ b/opendaylight/netconf/config-persister-impl/pom.xml @@ -90,6 +90,7 @@ org.opendaylight.controller.netconf.client, org.opendaylight.controller.netconf.util.osgi, org.opendaylight.controller.netconf.util.xml, + org.opendaylight.controller.netconf.util.messages, io.netty.channel, io.netty.channel.nio, io.netty.util.concurrent, diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java index 89c2703285..0b623baaa4 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java @@ -8,14 +8,24 @@ package org.opendaylight.controller.netconf.persist.impl; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import io.netty.channel.EventLoopGroup; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.concurrent.Immutable; + import org.opendaylight.controller.config.api.ConflictingVersionException; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.client.NetconfClient; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageAdditionalHeader; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.util.xml.XmlUtil; @@ -25,14 +35,8 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; -import javax.annotation.concurrent.Immutable; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; @Immutable public class ConfigPusher { @@ -127,10 +131,14 @@ public class ConfigPusher { long deadline = pollingStart + timeout; + String additionalHeader = NetconfMessageAdditionalHeader.toString("unknown", address.getAddress().getHostAddress(), + Integer.toString(address.getPort()), "tcp", Optional.of("persister")); + Set latestCapabilities = new HashSet<>(); while (System.currentTimeMillis() < deadline) { attempt++; - NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup); + NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, + nettyThreadgroup, additionalHeader); NetconfClient netconfClient; try { netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher); diff --git a/opendaylight/netconf/ietf-netconf-monitoring-extension/pom.xml b/opendaylight/netconf/ietf-netconf-monitoring-extension/pom.xml new file mode 100644 index 0000000000..aebaaebc1a --- /dev/null +++ b/opendaylight/netconf/ietf-netconf-monitoring-extension/pom.xml @@ -0,0 +1,101 @@ + + + netconf-subsystem + org.opendaylight.controller + 0.2.3-SNAPSHOT + + 4.0.0 + ietf-netconf-monitoring-extension + ${project.artifactId} + bundle + + + + org.opendaylight.controller + ietf-netconf-monitoring + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + + + + + org.opendaylight.yangtools + yang-maven-plugin + ${yangtools.version} + + + + generate-sources + + + src/main/yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + + + target/generated-sources/monitoring + + + + true + + + + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + ${yangtools.binding.version} + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + generate-sources + + add-source + + + + target/generated-sources/sal + + + + + + + + org.apache.felix + maven-bundle-plugin + + + + com.google.common.collect, + org.opendaylight.yangtools.yang.binding, + org.opendaylight.yangtools.yang.common, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004, + + + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210, + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/ietf-netconf-monitoring-extension/src/main/yang/ietf-netconf-monitoring-extension.yang b/opendaylight/netconf/ietf-netconf-monitoring-extension/src/main/yang/ietf-netconf-monitoring-extension.yang new file mode 100644 index 0000000000..e8f2ec324b --- /dev/null +++ b/opendaylight/netconf/ietf-netconf-monitoring-extension/src/main/yang/ietf-netconf-monitoring-extension.yang @@ -0,0 +1,31 @@ +module ietf-netconf-monitoring-extension { + + yang-version 1; + + namespace + "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring-extension"; + + prefix ncme; + + import ietf-netconf-monitoring { + prefix ncm; + } + + revision "2013-12-10" { + description "Initial revision."; + + } + + identity netconf-tcp { + base ncm:transport; + description + "NETCONF over TCP."; + } + + augment "/ncm:netconf-state/ncm:sessions/ncm:session" { + leaf session-identifier { + type string; + } + } + +} \ No newline at end of file diff --git a/opendaylight/netconf/netconf-api/pom.xml b/opendaylight/netconf/netconf-api/pom.xml index 0fce4748a4..856bd77c20 100644 --- a/opendaylight/netconf/netconf-api/pom.xml +++ b/opendaylight/netconf/netconf-api/pom.xml @@ -30,6 +30,11 @@ ${project.groupId} ietf-netconf-monitoring + + ${project.groupId} + ietf-netconf-monitoring-extension + ${project.version} + org.opendaylight.bgpcep diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java index a0fddd79f2..7877843ccb 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java @@ -8,9 +8,10 @@ package org.opendaylight.controller.netconf.api; -import com.google.common.base.Optional; import org.w3c.dom.Document; +import com.google.common.base.Optional; + /** * NetconfMessage represents a wrapper around org.w3c.dom.Document. Needed for * implementing ProtocolMessage interface. diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java index 6ac57a88c9..fc6f87db5d 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java @@ -13,8 +13,10 @@ import io.netty.channel.socket.SocketChannel; import io.netty.util.HashedWheelTimer; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; + import java.io.Closeable; import java.net.InetSocketAddress; + import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.NetconfTerminationReason; @@ -26,6 +28,8 @@ import org.opendaylight.protocol.framework.SessionListenerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Optional; + public class NetconfClientDispatcher extends AbstractDispatcher implements Closeable { private static final Logger logger = LoggerFactory.getLogger(NetconfClient.class); @@ -36,7 +40,13 @@ public class NetconfClientDispatcher extends AbstractDispatcherabsent()); + } + + public NetconfClientDispatcher(EventLoopGroup bossGroup, EventLoopGroup workerGroup, String additionalHeader) { + super(bossGroup, workerGroup); + timer = new HashedWheelTimer(); + this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.of(additionalHeader)); } public Future createClient(InetSocketAddress address, diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java index db0b953bdd..abfbdd526c 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java @@ -8,10 +8,13 @@ package org.opendaylight.controller.netconf.client; -import com.google.common.base.Preconditions; import io.netty.channel.Channel; import io.netty.util.Timer; import io.netty.util.concurrent.Promise; + +import java.io.IOException; +import java.io.InputStream; + import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.NetconfSessionPreferences; import org.opendaylight.controller.netconf.util.xml.XmlUtil; @@ -20,15 +23,18 @@ import org.opendaylight.protocol.framework.SessionNegotiator; import org.opendaylight.protocol.framework.SessionNegotiatorFactory; import org.xml.sax.SAXException; -import java.io.IOException; -import java.io.InputStream; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorFactory { private final Timer timer; - public NetconfClientSessionNegotiatorFactory(Timer timer) { + private final Optional additionalHeader; + + public NetconfClientSessionNegotiatorFactory(Timer timer, Optional additionalHeader) { this.timer = timer; + this.additionalHeader = additionalHeader; } private static NetconfMessage loadHelloMessageTemplate() { @@ -45,7 +51,11 @@ public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorF public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel, Promise promise) { // Hello message needs to be recreated every time - NetconfSessionPreferences proposal = new NetconfSessionPreferences(loadHelloMessageTemplate()); + NetconfMessage helloMessage = loadHelloMessageTemplate(); + if(this.additionalHeader.isPresent()) { + helloMessage = new NetconfMessage(helloMessage.getDocument(), additionalHeader.get()); + } + NetconfSessionPreferences proposal = new NetconfSessionPreferences(helloMessage); return new NetconfClientSessionNegotiator(proposal, promise, channel, timer, sessionListenerFactory.getSessionListener()); } diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java index 4de6cc35c0..ee07b3949d 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfSshClientDispatcher.java @@ -14,8 +14,10 @@ import io.netty.channel.socket.SocketChannel; import io.netty.util.HashedWheelTimer; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; + import java.io.IOException; import java.net.InetSocketAddress; + import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.NetconfTerminationReason; @@ -31,6 +33,8 @@ import org.opendaylight.protocol.framework.ReconnectStrategy; import org.opendaylight.protocol.framework.SessionListener; import org.opendaylight.protocol.framework.SessionListenerFactory; +import com.google.common.base.Optional; + public class NetconfSshClientDispatcher extends NetconfClientDispatcher { private AuthenticationHandler authHandler; @@ -42,7 +46,15 @@ public class NetconfSshClientDispatcher extends NetconfClientDispatcher { super(bossGroup, workerGroup); this.authHandler = authHandler; this.timer = new HashedWheelTimer(); - this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer); + this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.absent()); + } + + public NetconfSshClientDispatcher(AuthenticationHandler authHandler, EventLoopGroup bossGroup, + EventLoopGroup workerGroup, String additionalHeader) { + super(bossGroup, workerGroup, additionalHeader); + this.authHandler = authHandler; + this.timer = new HashedWheelTimer(); + this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer, Optional.of(additionalHeader)); } public Future createClient(InetSocketAddress address, diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml index e073aaca8d..b056f9be87 100644 --- a/opendaylight/netconf/netconf-impl/pom.xml +++ b/opendaylight/netconf/netconf-impl/pom.xml @@ -23,6 +23,11 @@ ${project.groupId} ietf-netconf-monitoring + + ${project.groupId} + ietf-netconf-monitoring-extension + ${project.version} + ${project.groupId} netconf-util @@ -150,6 +155,8 @@ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210, + org.opendaylight.yangtools.yang.binding, diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java index b692179429..4cc05b7b42 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java @@ -8,13 +8,21 @@ package org.opendaylight.controller.netconf.impl; -import com.google.common.base.Preconditions; import io.netty.channel.Channel; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; import org.opendaylight.protocol.framework.SessionListener; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.NetconfTcp; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1Builder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Transport; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session; @@ -25,10 +33,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types. import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.google.common.base.Preconditions; public class NetconfServerSession extends NetconfSession implements NetconfManagementSession { @@ -91,14 +96,18 @@ public class NetconfServerSession extends NetconfSession implements NetconfManag builder.setOutNotifications(new ZeroBasedCounter32(0L)); builder.setKey(new SessionKey(getSessionId())); + + Session1Builder builder1 = new Session1Builder(); + builder1.setSessionIdentifier(header.getSessionType()); + builder.addAugmentation(Session1.class, builder1.build()); + return builder.build(); } private Class getTransportForString(String transport) { switch(transport) { case "ssh" : return NetconfSsh.class; - // TODO what about tcp - case "tcp" : return NetconfSsh.class; + case "tcp" : return NetconfTcp.class; default: throw new IllegalArgumentException("Unknown transport type " + transport); } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java index 01ac018b3e..8ba4cdc052 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java @@ -8,28 +8,27 @@ package org.opendaylight.controller.netconf.impl; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import io.netty.channel.Channel; import io.netty.util.Timer; import io.netty.util.concurrent.Promise; + +import java.net.InetSocketAddress; + import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences; +import org.opendaylight.controller.netconf.impl.util.AdditionalHeaderUtil; import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator; import org.opendaylight.protocol.framework.SessionListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.google.common.base.Optional; public class NetconfServerSessionNegotiator extends AbstractNetconfSessionNegotiator { static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionNegotiator.class); - private static final AdditionalHeader DEFAULT_HEADER = new AdditionalHeader(); - protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences, Promise promise, Channel channel, Timer timer, SessionListener sessionListener) { super(sessionPreferences, promise, channel, timer, sessionListener); @@ -41,36 +40,28 @@ public class NetconfServerSessionNegotiator extends AdditionalHeader parsedHeader; if (additionalHeader.isPresent()) { - parsedHeader = new AdditionalHeader(additionalHeader.get()); + parsedHeader = AdditionalHeaderUtil.fromString(additionalHeader.get()); } else { - parsedHeader = DEFAULT_HEADER; + parsedHeader = new AdditionalHeader("unknown", ((InetSocketAddress)channel.localAddress()).getHostString(), + "tcp", "client"); } logger.debug("Additional header from hello parsed as {} from {}", parsedHeader, additionalHeader); return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId(), parsedHeader); } - static class AdditionalHeader { + public static class AdditionalHeader { - private static final Pattern pattern = Pattern - .compile("\\[(?[^;]+);(?
[0-9\\.]+)[:/](?[0-9]+);(?[a-z]+)[^\\]]+\\]"); private final String username; private final String address; private final String transport; + private final String sessionIdentifier; - public AdditionalHeader(String addHeaderAsString) { - addHeaderAsString = addHeaderAsString.trim(); - Matcher matcher = pattern.matcher(addHeaderAsString); - Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s", - addHeaderAsString, pattern); - this.username = matcher.group("username"); - this.address = matcher.group("address"); - this.transport = matcher.group("transport"); - } - - private AdditionalHeader() { - this.username = this.address = "unknown"; - this.transport = "ssh"; + public AdditionalHeader(String userName, String hostAddress, String transport, String sessionIdentifier) { + this.address = hostAddress; + this.username = userName; + this.transport = transport; + this.sessionIdentifier = sessionIdentifier; } String getUsername() { @@ -85,6 +76,10 @@ public class NetconfServerSessionNegotiator extends return transport; } + String getSessionType() { + return sessionIdentifier; + } + @Override public String toString() { final StringBuffer sb = new StringBuffer("AdditionalHeader{"); @@ -95,4 +90,5 @@ public class NetconfServerSessionNegotiator extends return sb.toString(); } } + } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/AdditionalHeaderUtil.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/AdditionalHeaderUtil.java new file mode 100644 index 0000000000..5c630dd343 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/AdditionalHeaderUtil.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 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.netconf.impl.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiator.AdditionalHeader; + +import com.google.common.base.Preconditions; + +public class AdditionalHeaderUtil { + + private static final Pattern pattern = Pattern + .compile("\\[(?[^;]+);(?
[0-9\\.]+)[:/](?[0-9]+);(?[a-z]+)[^\\]]+\\]"); + private static final Pattern customHeaderPattern = Pattern + .compile("\\[(?[^;]+);(?
[0-9\\.]+)[:/](?[0-9]+);(?[a-z]+);(?[a-z]+)[^\\]]+\\]"); + + public static AdditionalHeader fromString(String additionalHeader) { + additionalHeader = additionalHeader.trim(); + Matcher matcher = pattern.matcher(additionalHeader); + Matcher matcher2 = customHeaderPattern.matcher(additionalHeader); + Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s", + additionalHeader, pattern); + String username = matcher.group("username"); + String address = matcher.group("address"); + String transport = matcher.group("transport"); + String sessionIdentifier = "client"; + if (matcher2.matches()) { + sessionIdentifier = matcher2.group("sessionIdentifier"); + } + return new AdditionalHeader(username, address, transport, sessionIdentifier); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java index 2f8fac23f5..97d9a98b57 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java @@ -8,14 +8,16 @@ package org.opendaylight.controller.netconf.impl; import junit.framework.Assert; + import org.junit.Test; +import org.opendaylight.controller.netconf.impl.util.AdditionalHeaderUtil; public class AdditionalHeaderParserTest { @Test public void testParsing() throws Exception { String s = "[netconf;10.12.0.102:48528;ssh;;;;;;]"; - NetconfServerSessionNegotiator.AdditionalHeader header = new NetconfServerSessionNegotiator.AdditionalHeader(s); + NetconfServerSessionNegotiator.AdditionalHeader header = AdditionalHeaderUtil.fromString(s); Assert.assertEquals("netconf", header.getUsername()); Assert.assertEquals("10.12.0.102", header.getAddress()); Assert.assertEquals("ssh", header.getTransport()); @@ -24,7 +26,7 @@ public class AdditionalHeaderParserTest { @Test public void testParsing2() throws Exception { String s = "[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]"; - NetconfServerSessionNegotiator.AdditionalHeader header = new NetconfServerSessionNegotiator.AdditionalHeader(s); + NetconfServerSessionNegotiator.AdditionalHeader header = AdditionalHeaderUtil.fromString(s); Assert.assertEquals("tomas", header.getUsername()); Assert.assertEquals("10.0.0.0", header.getAddress()); Assert.assertEquals("tcp", header.getTransport()); @@ -33,6 +35,6 @@ public class AdditionalHeaderParserTest { @Test(expected = IllegalArgumentException.class) public void testParsingNoUsername() throws Exception { String s = "[10.12.0.102:48528;ssh;;;;;;]"; - new NetconfServerSessionNegotiator.AdditionalHeader(s); + AdditionalHeaderUtil.fromString(s); } } diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java index ce5233c494..8d3476f4b8 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java @@ -8,12 +8,31 @@ package org.opendaylight.controller.netconf.impl; -import com.google.common.base.Optional; -import com.google.common.collect.Sets; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; + +import java.io.DataOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.management.ObjectName; + import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.AfterClass; @@ -44,31 +63,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import javax.management.ObjectName; -import java.io.DataOutputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import com.google.common.base.Optional; +import com.google.common.collect.Sets; public class ConcurrentClientsTest { private static final int CONCURRENCY = 16; private static EventLoopGroup nettyGroup = new NioEventLoopGroup(); - public static final NetconfClientDispatcher NETCONF_CLIENT_DISPATCHER = new NetconfClientDispatcher( nettyGroup, nettyGroup); + public static final NetconfClientDispatcher NETCONF_CLIENT_DISPATCHER = + new NetconfClientDispatcher( nettyGroup, nettyGroup); @Mock private YangStoreService yangStoreService; diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java index 244e4ba4a9..3a7b7de7a0 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java @@ -7,14 +7,27 @@ */ package org.opendaylight.controller.netconf.it; -import com.google.common.base.Charsets; -import com.google.common.base.Optional; -import com.google.common.collect.Sets; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + import junit.framework.Assert; + import org.junit.Before; import org.junit.Test; import org.junit.matchers.JUnitMatchers; @@ -48,20 +61,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.collect.Sets; public class NetconfMonitoringITTest extends AbstractConfigTest { diff --git a/opendaylight/netconf/netconf-monitoring/pom.xml b/opendaylight/netconf/netconf-monitoring/pom.xml index 31e427191c..8e1e599c71 100644 --- a/opendaylight/netconf/netconf-monitoring/pom.xml +++ b/opendaylight/netconf/netconf-monitoring/pom.xml @@ -79,10 +79,13 @@ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924, org.osgi.util.tracker, + org.opendaylight.yangtools.yang.common, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004, org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas, + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210, + org.opendaylight.yangtools.yang.binding, diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java index 25fb5d44dc..55aee72fda 100644 --- a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java @@ -7,13 +7,13 @@ */ package org.opendaylight.controller.netconf.monitoring.xml.model; -import com.google.common.base.Preconditions; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session; - import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session; +import org.opendaylight.yangtools.yang.common.QName; + final class MonitoringSession { @XmlTransient @@ -67,8 +67,17 @@ final class MonitoringSession { @XmlElement(name = "transport") public String getTransport() { - Preconditions.checkState(managementSession.getTransport() == NetconfSsh.class); - return "netconf-ssh"; + try { + QName qName = (QName) managementSession.getTransport().getField("QNAME").get(null); + return qName.getLocalName(); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + throw new IllegalArgumentException("Unknown transport type " + managementSession.getTransport(), e); + } + } + + @XmlElement(name= "session-identifier") + public String getSessionType() { + return managementSession.getAugmentation(Session1.class).getSessionIdentifier(); } @XmlElement(name = "username") diff --git a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java index cb6e59f83f..1e3f343624 100644 --- a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java +++ b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java @@ -7,13 +7,18 @@ */ package org.opendaylight.controller.netconf.monitoring.xml; -import com.google.common.collect.Lists; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import java.util.Date; + import org.junit.Test; import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService; import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder; @@ -25,10 +30,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types. import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.ZeroBasedCounter32; import org.w3c.dom.Element; -import java.util.Date; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import com.google.common.collect.Lists; public class JaxBSerializerTest { @@ -54,6 +56,8 @@ public class JaxBSerializerTest { private Session getMockSession() { Session mocked = mock(Session.class); + Session1 mockedSession1 = mock(Session1.class); + doReturn("client").when(mockedSession1).getSessionIdentifier(); doReturn(1L).when(mocked).getSessionId(); doReturn(new DateAndTime(new Date().toString())).when(mocked).getLoginTime(); doReturn(new Host(new DomainName("address/port"))).when(mocked).getSourceHost(); @@ -63,6 +67,7 @@ public class JaxBSerializerTest { doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutRpcErrors(); doReturn(NetconfSsh.class).when(mocked).getTransport(); doReturn("username").when(mocked).getUsername(); + doReturn(mockedSession1).when(mocked).getAugmentation(Session1.class); return mocked; } } diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java index 95d2feb65c..e30ce5b47e 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java @@ -10,8 +10,9 @@ package org.opendaylight.controller.netconf.util; import com.google.common.base.Optional; import com.google.common.base.Preconditions; - import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; import io.netty.handler.ssl.SslHandler; import io.netty.util.Timeout; import io.netty.util.Timer; @@ -19,9 +20,8 @@ import io.netty.util.TimerTask; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise; - -import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.NetconfSessionPreferences; import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory; import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator; @@ -44,11 +44,14 @@ public abstract class AbstractNetconfSessionNegotiator

sslHandler = getSslHandler(channel); if (sslHandler.isPresent()) { Future future = sslHandler.get().handshakeFuture(); @@ -94,10 +97,25 @@ public abstract class AbstractNetconfSessionNegotiator

+ * It has pattern "[username; host-address:port; transport; session-identifier;]" + * username - name of account on a remote + * host-address - client's IP address + * port - port number + * transport - tcp, ssh + * session-identifier - persister, client + * Session-identifier is optional, others mandatory. + * + */ +public class NetconfMessageAdditionalHeader { + + private static final String SC = ";"; + + public static String toString(String userName, String hostAddress, String port, String transport, + Optional sessionIdentifier) { + Preconditions.checkNotNull(userName); + Preconditions.checkNotNull(hostAddress); + Preconditions.checkNotNull(port); + Preconditions.checkNotNull(transport); + String identifier = sessionIdentifier.isPresent() ? sessionIdentifier.get() : ""; + return "[" + userName + SC + hostAddress + ":" + port + SC + transport + SC + identifier + SC + "]" + + System.lineSeparator(); + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java index 891d40cf74..526708ab58 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java @@ -8,9 +8,12 @@ package org.opendaylight.controller.netconf.util.messages; -import com.google.common.base.Charsets; -import com.google.common.base.Optional; -import com.google.common.collect.Lists; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; + import org.opendaylight.controller.netconf.api.NetconfDeserializerException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.util.xml.XmlUtil; @@ -24,11 +27,9 @@ import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.xml.sax.SAXException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; /** * NetconfMessageFactory for (de)serializing DOM documents. @@ -114,7 +115,14 @@ public final class NetconfMessageFactory implements ProtocolMessageFactory../../third-party/com.siemens.ct.exi netconf-monitoring ietf-netconf-monitoring + ietf-netconf-monitoring-extension diff --git a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java index f33fa18e29..16c09fe3f6 100644 --- a/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java +++ b/opendaylight/switchmanager/api/src/main/java/org/opendaylight/controller/switchmanager/Subnet.java @@ -244,10 +244,7 @@ public class Subnet implements Cloneable, Serializable { if (p == null) { return false; } - if (this.isFlatLayer2()) { - return true; - } - return this.nodeConnectors.contains(p); + return isFlatLayer2() || nodeConnectors.contains(p); } public boolean isMutualExclusive(Subnet otherSubnet) {