From: Devin Avery Date: Tue, 22 Jul 2014 13:43:37 +0000 (+0000) Subject: Merge "API Usability: Introduced type capture for Transaction Factory" X-Git-Tag: release/helium~459 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=f8225a74b7c11db1ebf77ab9de998c2e953f8ed8;hp=fcc36c9530a5615289b9ee697f05e3862f948f8f Merge "API Usability: Introduced type capture for Transaction Factory" --- diff --git a/features/base/src/main/resources/features.xml b/features/base/src/main/resources/features.xml index 60286175c1..b9107b9f62 100644 --- a/features/base/src/main/resources/features.xml +++ b/features/base/src/main/resources/features.xml @@ -79,6 +79,8 @@ mvn:eclipselink/javax.persistence/2.0.4.v201112161009 mvn:eclipselink/javax.resource/1.5.0.v200906010428 + mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/2.5.0 + mvn:org.eclipse.persistence/org.eclipse.persistence.core/2.5.0 http diff --git a/opendaylight/config/feature/pom.xml b/features/config/pom.xml similarity index 97% rename from opendaylight/config/feature/pom.xml rename to features/config/pom.xml index aaf33ccc0f..01a4ea74a2 100644 --- a/opendaylight/config/feature/pom.xml +++ b/features/config/pom.xml @@ -5,6 +5,7 @@ org.opendaylight.controller config-subsystem 0.2.5-SNAPSHOT + ../../opendaylight/config/ config-features diff --git a/features/config/src/main/resources/features.xml b/features/config/src/main/resources/features.xml new file mode 100644 index 0000000000..7c11b5b18b --- /dev/null +++ b/features/config/src/main/resources/features.xml @@ -0,0 +1,82 @@ + + + + mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features + mvn:org.opendaylight.controller/netconf-features/${netconf.version}/xml/features + + + odl-config-netconf-connector + odl-config-persister + odl-netconf-impl + + + + yangtools-concepts + yangtools-binding + yangtools-binding-generator + odl-mdsal-commons + odl-config-api + mvn:org.opendaylight.controller/config-util/${project.version} + mvn:org.opendaylight.controller/yang-jmx-generator/${project.version} + mvn:org.opendaylight.controller/shutdown-api/${project.version} + mvn:org.opendaylight.controller/shutdown-impl/${project.version} + mvn:org.osgi/org.osgi.core/${osgi.core.version} + wrap:mvn:com.google.guava/guava/${guava.version} + mvn:org.javassist/javassist/${javassist.version} + + + odl-config-core + mvn:org.opendaylight.controller/config-manager/${project.version} + + + odl-netconf-api + odl-config-api + yangtools-binding-generator + mvn:org.opendaylight.controller/config-persister-api/${project.version} + mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version} + mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version} + mvn:org.opendaylight.controller/config-persister-impl/${project.version} + + mvn:org.opendaylight.controller/netconf-util/${netconf.version} + mvn:org.opendaylight.controller/netconf-mapping-api/${netconf.version} + + mvn:com.google.guava/guava/${guava.version} + mvn:commons-io/commons-io/${commons.io.version} + mvn:org.apache.commons/commons-lang3/${commons.lang3.version} + mvn:org.eclipse.persistence/org.eclipse.persistence.core/${eclipse.persistence.version} + mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/${eclipse.persistence.version} + + + mvn:org.opendaylight.controller/config-api/${project.version} + + + yangtools-concepts + yangtools-binding + + + + mvn:org.opendaylight.controller/netty-config-api/${project.version} + + + mvn:io.netty/netty-transport/${netty.version} + mvn:io.netty/netty-common/${netty.version} + mvn:io.netty/netty-buffer/${netty.version} + + odl-config-api + + + odl-config-netty-config-api + mvn:org.opendaylight.controller/netty-event-executor-config/${project.version} + mvn:org.opendaylight.controller/netty-threadgroup-config/${project.version} + mvn:org.opendaylight.controller/netty-timer-config/${project.version} + mvn:org.opendaylight.controller/threadpool-config-api/${project.version} + mvn:org.opendaylight.controller/threadpool-config-impl/${project.version} + odl-config-startup + + + mvn:org.opendaylight.controller/netconf-config-dispatcher/${project.version} + + + \ No newline at end of file diff --git a/opendaylight/md-sal/feature/pom.xml b/features/mdsal/pom.xml similarity index 97% rename from opendaylight/md-sal/feature/pom.xml rename to features/mdsal/pom.xml index c08c3e5606..2983c5efab 100644 --- a/opendaylight/md-sal/feature/pom.xml +++ b/features/mdsal/pom.xml @@ -5,6 +5,7 @@ org.opendaylight.controller sal-parent 1.1-SNAPSHOT + ../../opendaylight/md-sal mdsal-features diff --git a/opendaylight/md-sal/feature/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml similarity index 56% rename from opendaylight/md-sal/feature/src/main/resources/features.xml rename to features/mdsal/src/main/resources/features.xml index 16b4574037..7d393bc64c 100644 --- a/opendaylight/md-sal/feature/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -9,20 +9,20 @@ odl-mdsal-restconf - yangtools-concepts - yangtools-binding + yangtools-data-binding mvn:org.opendaylight.controller/sal-common/${project.version} mvn:org.opendaylight.controller/sal-common-api/${project.version} mvn:org.opendaylight.controller/sal-common-impl/${project.version} mvn:org.opendaylight.controller/sal-common-util/${project.version} - wrap:mvn:com.google.guava/guava/${guava.version} - wrap:mvn:org.eclipse.xtend/org.eclipse.xtend.lib/${xtend.version} yangtools-concepts yangtools-binding odl-mdsal-commons - odl-config-subsystem + odl-config-core + odl-config-manager + odl-config-api + odl-config-persister mvn:org.opendaylight.controller/sal-core-api/${project.version} mvn:org.opendaylight.controller/sal-core-spi/${project.version} mvn:org.opendaylight.controller/sal-broker-impl/${project.version} @@ -31,6 +31,7 @@ mvn:org.opendaylight.controller/sal-binding-broker-impl/${project.version} mvn:org.opendaylight.controller/sal-binding-util/${project.version} mvn:org.opendaylight.controller/sal-connector-api/${project.version} + mvn:org.opendaylight.controller/sal-inmemory-datastore/${project.version} odl-mdsal-broker @@ -46,4 +47,32 @@ wrap:mvn:io.netty/netty-handler/${netty.version} wrap:mvn:io.netty/netty-transport/${netty.version} + + mvn:org.opendaylight.controller.model/model-flow-base/${project.version} + mvn:org.opendaylight.controller.model/model-flow-management/${project.version} + mvn:org.opendaylight.controller.model/model-flow-service/${project.version} + mvn:org.opendaylight.controller.model/model-flow-statistics/${project.version} + mvn:org.opendaylight.controller.model/model-inventory/${project.version} + mvn:org.opendaylight.controller.model/model-topology/${project.version} + + + yangtools-concepts + yangtools-binding + odl-mdsal-broker + odl-mdsal-all + mvn:org.opendaylight.controller.samples/sample-toaster/${project.version} + mvn:org.opendaylight.controller.samples/sample-toaster-consumer/${project.version} + mvn:org.opendaylight.controller.samples/sample-toaster-provider/${project.version} + + + mvn:org.opendaylight.controller/sal-netconf-connector/${project.version} + mvn:org.opendaylight.controller/sal-restconf-broker/${project.version} + mvn:org.opendaylight.controller/sal-remote/${project.version} + mvn:org.opendaylight.controller.md/topology-manager/${project.version} + mvn:org.opendaylight.controller.md/topology-lldp-discovery/${project.version} + mvn:org.opendaylight.controller.md/statistics-manager/${project.version} + mvn:org.opendaylight.controller.md/inventory-manager/${project.version} + mvn:org.opendaylight.controller.md/forwardingrules-manager/${project.version} + + diff --git a/features/netconf/pom.xml b/features/netconf/pom.xml new file mode 100644 index 0000000000..457fc64edd --- /dev/null +++ b/features/netconf/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.opendaylight.controller + netconf-subsystem + 0.2.5-SNAPSHOT + ../../opendaylight/netconf + + netconf-features + + pom + + + features.xml + + + + + + + + true + src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + filter + + resources + + generate-resources + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/${features.file} + xml + features + + + + + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + + diff --git a/features/netconf/src/main/resources/features.xml b/features/netconf/src/main/resources/features.xml new file mode 100644 index 0000000000..50a537b50a --- /dev/null +++ b/features/netconf/src/main/resources/features.xml @@ -0,0 +1,59 @@ + + + + mvn:org.opendaylight.controller/features-odl-protocol-framework/${protocol-framework.version}/xml/features + mvn:org.opendaylight.controller/config-features/${config.version}/xml/features + + + mvn:org.opendaylight.controller/netconf-api/${project.version} + mvn:org.opendaylight.controller/ietf-netconf-monitoring/${project.version} + mvn:org.opendaylight.controller/ietf-netconf-monitoring-extension/${project.version} + odl-protocol-framework + mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version} + mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf-yang-types.version} + + + odl-netconf-api + mvn:org.opendaylight.controller/netconf-mapping-api/${project.version} + + + odl-netconf-mapping-api + mvn:org.opendaylight.controller/netconf-util/${project.version} + + + odl-config-manager + mvn:org.opendaylight.controller/config-netconf-connector/${project.version} + odl-netconf-api + odl-netconf-mapping-api + odl-netconf-util + + + + mvn:org.opendaylight.controller/netconf-impl/${project.version} + odl-netconf-api + odl-netconf-mapping-api + odl-netconf-util + odl-netconf-netty-util + + + mvn:org.opendaylight.controller/netconf-netty-util/${project.version} + odl-netconf-api + odl-netconf-mapping-api + odl-netconf-util + mvn:org.opendaylight.controller.thirdparty/ganymed/${ganymed.version} + mvn:org.openexi/nagasena/${exi.nagasena.version} + mvn:io.netty/netty-codec/${netty.version} + mvn:io.netty/netty-handler/${netty.version} + mvn:io.netty/netty-common/${netty.version} + mvn:io.netty/netty-buffer/${netty.version} + mvn:io.netty/netty-transport/${netty.version} + + + mvn:org.opendaylight.controller/netconf-client/${project.version} + mvn:org.opendaylight.controller/netconf-monitoring/${project.version} + mvn:org.opendaylight.controller/netconf-tcp/${project.version} + + + \ No newline at end of file diff --git a/features/pom.xml b/features/pom.xml new file mode 100644 index 0000000000..dce47faea6 --- /dev/null +++ b/features/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.2-SNAPSHOT + ../opendaylight/commons/opendaylight + + features-controller + pom + + 3.0 + + + config + mdsal + netconf + protocol-framework + + diff --git a/features/protocol-framework/pom.xml b/features/protocol-framework/pom.xml new file mode 100644 index 0000000000..f0208d6452 --- /dev/null +++ b/features/protocol-framework/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.2-SNAPSHOT + ../../opendaylight/commons/opendaylight + + features-odl-protocol-framework + ${protocol-framework.version} + pom + + + features.xml + + + + + + + + true + src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + filter + + resources + + generate-resources + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/${features.file} + xml + features + + + + + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + + diff --git a/features/protocol-framework/src/main/resources/features.xml b/features/protocol-framework/src/main/resources/features.xml new file mode 100644 index 0000000000..d2560f5cb0 --- /dev/null +++ b/features/protocol-framework/src/main/resources/features.xml @@ -0,0 +1,12 @@ + + + + mvn:org.opendaylight.controller/config-features/${config.version}/xml/features + + mvn:org.opendaylight.controller/protocol-framework/${protocol-framework.version} + odl-config-api + odl-config-netty-config-api + + \ No newline at end of file diff --git a/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/jaxb/Context.java b/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/jaxb/Context.java index dbe0745725..cec780c9ec 100644 --- a/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/jaxb/Context.java +++ b/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/jaxb/Context.java @@ -69,9 +69,10 @@ public class Context { initialized = true; } - public List findMatchingFilters(String pathInfo) { + public List findMatchingFilters(String path) { + logger.trace("findMatchingFilters({})", path); checkState(initialized, "Not initialized"); - return urlMatcher.findMatchingFilters(pathInfo); + return urlMatcher.findMatchingFilters(path); } @XmlAttribute(name = "path") diff --git a/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/FilterProcessor.java b/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/FilterProcessor.java index dc3e9dcd49..ae9d79a63d 100644 --- a/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/FilterProcessor.java +++ b/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/FilterProcessor.java @@ -37,14 +37,14 @@ public class FilterProcessor { throws IOException, ServletException { String contextPath = request.getContext().getPath(); - String pathInfo = request.getPathInfo(); + String path = request.getDecodedRequestURI(); Optional maybeContext = host.findContext(contextPath); - logger.trace("Processing context {} path {}, found {}", contextPath, pathInfo, maybeContext); + logger.trace("Processing context {} path {}, found {}", contextPath, path, maybeContext); if (maybeContext.isPresent()) { // process filters Context context = maybeContext.get(); - List matchingFilters = context.findMatchingFilters(pathInfo); + List matchingFilters = context.findMatchingFilters(path); FilterChain fromLast = nextValveFilterChain; ListIterator it = matchingFilters.listIterator(matchingFilters.size()); final boolean trace = logger.isTraceEnabled(); diff --git a/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/UrlMatcher.java b/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/UrlMatcher.java index 9535fb1f70..210326f5d1 100644 --- a/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/UrlMatcher.java +++ b/opendaylight/commons/filter-valve/src/main/java/org/opendaylight/controller/filtervalve/cors/model/UrlMatcher.java @@ -62,31 +62,31 @@ public class UrlMatcher { /** * Find filters matching path * - * @param pathInfo as returned by request.getPathInfo() + * @param path relative and decoded path to resource * @return list of matching filters */ - public List findMatchingFilters(String pathInfo) { - checkNotNull(pathInfo); + public List findMatchingFilters(String path) { + checkNotNull(path); TreeMap sortedMap = new TreeMap<>(); // add matching prefixes for (Entry> prefixEntry : prefixMap.entrySet()) { - if (pathInfo.startsWith(prefixEntry.getKey())) { + if (path.startsWith(prefixEntry.getKey())) { put(sortedMap, prefixEntry.getValue()); } } // add matching suffixes for (Entry> suffixEntry : suffixMap.entrySet()) { - if (pathInfo.endsWith(suffixEntry.getKey())) { + if (path.endsWith(suffixEntry.getKey())) { put(sortedMap, suffixEntry.getValue()); } } // add exact match - Entry exactMatch = exactMatchMap.get(pathInfo); + Entry exactMatch = exactMatchMap.get(path); if (exactMatch != null) { put(sortedMap, exactMatch); } ArrayList filters = new ArrayList<>(sortedMap.values()); - logger.trace("Matching filters for path {} are {}", pathInfo, filters); + logger.trace("Matching filters for path {} are {}", path, filters); return filters; } diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 28c23c8259..163c64ed52 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -91,6 +91,7 @@ 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT 0.6.0-SNAPSHOT + 1.1-SNAPSHOT 0.5.2-SNAPSHOT 0.5.2-SNAPSHOT 0.4.2-SNAPSHOT @@ -1498,7 +1499,7 @@ org.opendaylight.controller.thirdparty ganymed - 1.1-SNAPSHOT + ${ganymed.version} diff --git a/opendaylight/config/feature/src/main/resources/features.xml b/opendaylight/config/feature/src/main/resources/features.xml deleted file mode 100644 index a84a74305f..0000000000 --- a/opendaylight/config/feature/src/main/resources/features.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - odl-config-subsystem - - - - yangtools-concepts - yangtools-binding - yangtools-binding-generator - odl-mdsal-commons - mvn:org.opendaylight.controller/config-api/${project.version} - mvn:org.opendaylight.controller/config-util/${project.version} - mvn:org.opendaylight.controller/config-manager/${project.version} - mvn:org.opendaylight.controller/yang-jmx-generator/${project.version} - mvn:org.opendaylight.controller/config-persister-api/${project.version} - mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version} - mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version} - mvn:org.opendaylight.controller/shutdown-api/${project.version} - mvn:org.opendaylight.controller/shutdown-impl/${project.version} - mvn:org.osgi/org.osgi.core/${osgi.core.version} - wrap:mvn:com.google.guava/guava/${guava.version} - mvn:org.javassist/javassist/${javassist.version} - - \ No newline at end of file diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index a8a4c87a4e..66bb01f051 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -39,7 +39,6 @@ shutdown-impl netconf-config-dispatcher config-module-archetype - feature diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java index 3dfa6e2bc7..5036399828 100644 --- a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java +++ b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/threadpool/util/FlexibleThreadPoolWrapper.java @@ -10,15 +10,20 @@ package org.opendaylight.controller.config.threadpool.util; import java.io.Closeable; import java.io.IOException; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.opendaylight.controller.config.threadpool.ThreadPool; +import com.google.common.base.Optional; + /** * Implementation of {@link ThreadPool} using flexible number of threads wraps * {@link ExecutorService}. @@ -28,12 +33,33 @@ public class FlexibleThreadPoolWrapper implements ThreadPool, Closeable { public FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit, ThreadFactory threadFactory) { + this(minThreadCount, maxThreadCount, keepAlive, timeUnit, threadFactory, getQueue(Optional.absent())); + } + + public FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit, + ThreadFactory threadFactory, Optional queueCapacity) { + this(minThreadCount, maxThreadCount, keepAlive, timeUnit, threadFactory, getQueue(queueCapacity)); + } + + private FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit, + ThreadFactory threadFactory, BlockingQueue queue) { executor = new ThreadPoolExecutor(minThreadCount, maxThreadCount, keepAlive, timeUnit, - new SynchronousQueue(), threadFactory); + queue, threadFactory, new FlexibleRejectionHandler()); executor.prestartAllCoreThreads(); } + /** + * Overriding the queue: + * ThreadPoolExecutor would not create new threads if the queue is not full, thus adding + * occurs in RejectedExecutionHandler. + * This impl saturates threadpool first, then queue. When both are full caller will get blocked. + */ + private static ForwardingBlockingQueue getQueue(Optional capacity) { + final BlockingQueue delegate = capacity.isPresent() ? new LinkedBlockingQueue(capacity.get()) : new LinkedBlockingQueue(); + return new ForwardingBlockingQueue(delegate); + } + @Override public ExecutorService getExecutor() { return Executors.unconfigurableExecutorService(executor); @@ -77,4 +103,37 @@ public class FlexibleThreadPoolWrapper implements ThreadPool, Closeable { executor.shutdown(); } + /** + * if the max threads are met, then it will raise a rejectedExecution. We then push to the queue. + */ + private static class FlexibleRejectionHandler implements RejectedExecutionHandler { + @Override + public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { + try { + executor.getQueue().put(r); + } catch (InterruptedException e) { + throw new RejectedExecutionException("Interrupted while waiting on the queue", e); + } + } + } + + private static class ForwardingBlockingQueue extends com.google.common.util.concurrent.ForwardingBlockingQueue { + private final BlockingQueue delegate; + + public ForwardingBlockingQueue(BlockingQueue delegate) { + this.delegate = delegate; + } + + @Override + protected BlockingQueue delegate() { + return delegate; + } + + @Override + public boolean offer(final Runnable r) { + // ThreadPoolExecutor will spawn a new thread after core size is reached only + // if the queue.offer returns false. + return false; + } + } } diff --git a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/flexible/FlexibleThreadPoolModule.java b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/flexible/FlexibleThreadPoolModule.java index 94639d43c0..d6abe168fb 100644 --- a/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/flexible/FlexibleThreadPoolModule.java +++ b/opendaylight/config/threadpool-config-impl/src/main/java/org/opendaylight/controller/config/yang/threadpool/impl/flexible/FlexibleThreadPoolModule.java @@ -17,6 +17,7 @@ */ package org.opendaylight.controller.config.yang.threadpool.impl.flexible; +import com.google.common.base.Optional; import java.util.concurrent.TimeUnit; import org.opendaylight.controller.config.api.JmxAttributeValidationException; @@ -50,11 +51,15 @@ public final class FlexibleThreadPoolModule extends org.opendaylight.controller. JmxAttributeValidationException.checkNotNull(getMaxThreadCount(), maxThreadCountJmxAttribute); JmxAttributeValidationException.checkCondition(getMaxThreadCount() > 0, "must be greater than zero", maxThreadCountJmxAttribute); + + if(getQueueCapacity() != null) { + JmxAttributeValidationException.checkCondition(getQueueCapacity() > 0, "Queue capacity cannot be < 1", queueCapacityJmxAttribute); + } } @Override public java.lang.AutoCloseable createInstance() { return new FlexibleThreadPoolWrapper(getMinThreadCount(), getMaxThreadCount(), getKeepAliveMillis(), - TimeUnit.MILLISECONDS, getThreadFactoryDependency()); + TimeUnit.MILLISECONDS, getThreadFactoryDependency(), Optional.fromNullable(getQueueCapacity())); } } diff --git a/opendaylight/config/threadpool-config-impl/src/main/yang/threadpool-impl-flexible.yang b/opendaylight/config/threadpool-config-impl/src/main/yang/threadpool-impl-flexible.yang index be275ef487..c124f6388f 100644 --- a/opendaylight/config/threadpool-config-impl/src/main/yang/threadpool-impl-flexible.yang +++ b/opendaylight/config/threadpool-config-impl/src/main/yang/threadpool-impl-flexible.yang @@ -46,6 +46,12 @@ module threadpool-impl-flexible { type uint32; } + leaf queueCapacity { + type uint16; + mandatory false; + description "Capacity of queue that holds waiting tasks"; + } + container threadFactory { uses config:service-ref { refine type { diff --git a/opendaylight/distribution/opendaylight-karaf/pom.xml b/opendaylight/distribution/opendaylight-karaf/pom.xml index 221bfa78e8..4ef0b8e86b 100644 --- a/opendaylight/distribution/opendaylight-karaf/pom.xml +++ b/opendaylight/distribution/opendaylight-karaf/pom.xml @@ -61,6 +61,22 @@ kar runtime + + org.opendaylight.controller + features-odl-protocol-framework + ${protocol-framework.version} + features + xml + runtime + + + org.opendaylight.controller + netconf-features + ${netconf.version} + features + xml + runtime + org.opendaylight.controller config-features @@ -85,14 +101,6 @@ xml runtime - - org.opendaylight.yangtools - features-yangtools - ${yangtools.version} - features - xml - runtime - diff --git a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/02-clustering.xml b/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/02-clustering.xml deleted file mode 100644 index 7853b8681b..0000000000 --- a/opendaylight/distribution/opendaylight-karaf/src/main/resources/configuration/initial/02-clustering.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - prefix:remote-zeromq-rpc-server - remoter - 5666 - - prefix:dom-broker-osgi-registry - dom-broker - - - - - - - - - - urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28 - urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc?module=odl-sal-dom-rpc-remote-cfg&revision=2013-10-28 - - - diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml index 8fedbe4d4c..f81a332ab6 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml @@ -12,7 +12,7 @@ - + prefix:netconf-client-dispatcher global-netconf-dispatcher @@ -30,24 +30,25 @@ - + prefix:threadfactory-naming global-netconf-processing-executor-threadfactory remote-connector-processing-executor - - + + prefix:threadpool-flexible global-netconf-processing-executor 1 4 600000 + prefix:threadfactory global-netconf-processing-executor-threadfactory - + diff --git a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java index c89cfc1b9e..e2b6642501 100644 --- a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java +++ b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java @@ -867,6 +867,7 @@ public class FlowConfig extends ConfigurationObject implements Serializable { } continue; } + sstr = Pattern.compile(ActionType.SET_NEXT_HOP.toString() + "=(.*)").matcher(actiongrp); if (sstr.matches()) { if (!NetUtils.isIPAddressValid(sstr.group(1))) { @@ -875,7 +876,57 @@ public class FlowConfig extends ConfigurationObject implements Serializable { } continue; } - } + + sstr = Pattern.compile(ActionType.OUTPUT + "=(.*)").matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.DROP.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.LOOPBACK.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.FLOOD.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.FLOOD_ALL.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.HW_PATH.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.CONTROLLER.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + sstr = Pattern.compile(ActionType.POP_VLAN.toString()).matcher(actiongrp); + if (sstr.matches()) { + continue; + } + + // If execution reached here, it means there is an Action + // which is not recognized by the controller. Return error + + return new Status(StatusCode.BADREQUEST, String.format("%s is an UnSupported Action", actiongrp)); + } } catch (NumberFormatException e) { return new Status(StatusCode.BADREQUEST, String.format("Invalid number format %s", e.getMessage())); } diff --git a/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java b/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java index 685ccdb7c4..3c367b9af4 100644 --- a/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java +++ b/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java @@ -760,9 +760,9 @@ public class frmTest { actions.add(ActionType.SET_NW_SRC.toString() + "=1.1.1.1"); actions.add(ActionType.SET_NW_DST.toString() + "=2.2.2.2"); actions.add(ActionType.CONTROLLER.toString()); - actions.add(ActionType.SET_NW_TOS.toString() + "1"); - actions.add(ActionType.SET_TP_SRC.toString() + "60"); - actions.add(ActionType.SET_TP_DST.toString() + "8080"); + actions.add(ActionType.SET_NW_TOS.toString() + "=1"); + actions.add(ActionType.SET_TP_SRC.toString() + "=60"); + actions.add(ActionType.SET_TP_DST.toString() + "=8080"); actions.add(ActionType.SET_NEXT_HOP.toString() + "=1.1.1.1"); return actions; diff --git a/opendaylight/md-sal/compatibility/flow-management-compatibility/src/main/java/org/opendaylight/controller/md/frm/compatibility/FRMRuntimeDataProvider.java b/opendaylight/md-sal/compatibility/flow-management-compatibility/src/main/java/org/opendaylight/controller/md/frm/compatibility/FRMRuntimeDataProvider.java index be3add1042..ff68176f1f 100644 --- a/opendaylight/md-sal/compatibility/flow-management-compatibility/src/main/java/org/opendaylight/controller/md/frm/compatibility/FRMRuntimeDataProvider.java +++ b/opendaylight/md-sal/compatibility/flow-management-compatibility/src/main/java/org/opendaylight/controller/md/frm/compatibility/FRMRuntimeDataProvider.java @@ -7,8 +7,6 @@ */ package org.opendaylight.controller.md.frm.compatibility; -import java.util.Collections; - import org.opendaylight.controller.forwardingrulesmanager.FlowConfig; import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager; import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener; @@ -17,7 +15,6 @@ import org.opendaylight.controller.md.sal.common.api.data.DataModification; import org.opendaylight.controller.sal.binding.api.data.DataProviderService; import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider; import org.opendaylight.controller.sal.common.util.Arguments; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowKey; import org.opendaylight.yangtools.concepts.Registration; @@ -26,8 +23,8 @@ import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import com.google.common.base.Objects; import com.google.common.base.Preconditions; @@ -89,7 +86,7 @@ public class FRMRuntimeDataProvider implements RuntimeDataProvider, DataCommitHa this.manager.removeStaticFlow(flow.getName(), flow.getNode()); this.manager.addStaticFlow(flow); } - return Rpcs. getRpcResult(true, null, Collections. emptySet()); + return RpcResultBuilder. success().build(); } public RpcResult rollback(final FlowCommitTransaction transaction) { diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowServiceAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowServiceAdapter.java index 01d75acfe6..56c7afb253 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowServiceAdapter.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowServiceAdapter.java @@ -10,7 +10,6 @@ package org.opendaylight.controller.sal.compatibility.adsal; import java.math.BigInteger; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.compatibility.InventoryMapping; import org.opendaylight.controller.sal.compatibility.ToSalConversionsUtils; import org.opendaylight.controller.sal.flowprogrammer.Flow; @@ -32,6 +31,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103. import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,7 +70,8 @@ public class FlowServiceAdapter implements SalFlowService, IFlowProgrammerListen AddFlowOutputBuilder builder = new AddFlowOutputBuilder(); builder.setTransactionId(new TransactionId(BigInteger.valueOf(status.getRequestId()))); AddFlowOutput rpcResultType = builder.build(); - return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null)); + return Futures.immediateFuture(RpcResultBuilder.status(status.isSuccess()) + .withResult(rpcResultType).build()); } @Override @@ -84,7 +85,8 @@ public class FlowServiceAdapter implements SalFlowService, IFlowProgrammerListen RemoveFlowOutputBuilder builder = new RemoveFlowOutputBuilder(); builder.setTransactionId(new TransactionId(BigInteger.valueOf(status.getRequestId()))); RemoveFlowOutput rpcResultType = builder.build(); - return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null)); + return Futures.immediateFuture(RpcResultBuilder.status(status.isSuccess()) + .withResult(rpcResultType).build()); } diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowStatisticsAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowStatisticsAdapter.java index c5cbecabed..e63cb54b86 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowStatisticsAdapter.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/adsal/FlowStatisticsAdapter.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.concurrent.Future; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.compatibility.FromSalConversionsUtils; import org.opendaylight.controller.sal.compatibility.InventoryMapping; import org.opendaylight.controller.sal.compatibility.NodeMapping; @@ -60,6 +59,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.n import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMapBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -107,7 +107,8 @@ public class FlowStatisticsAdapter implements OpendaylightFlowStatisticsService, LOG.error(e.getMessage()); } - return Futures.immediateFuture(Rpcs.getRpcResult(rpcResultBool, rpcResultType, null)); + return Futures.immediateFuture(RpcResultBuilder. + status(rpcResultBool).withResult(rpcResultType).build()); } /** @@ -133,7 +134,8 @@ public class FlowStatisticsAdapter implements OpendaylightFlowStatisticsService, LOG.error(e.getMessage()); } - return Futures.immediateFuture(Rpcs.getRpcResult(rpcResultBool, rpcResultType, null)); + return Futures.immediateFuture(RpcResultBuilder. + status(rpcResultBool).withResult(rpcResultType).build()); } @Override @@ -154,7 +156,8 @@ public class FlowStatisticsAdapter implements OpendaylightFlowStatisticsService, LOG.error(e.getMessage()); } - return Futures.immediateFuture(Rpcs.getRpcResult(rpcResultBool, rpcResultType, null)); + return Futures.immediateFuture(RpcResultBuilder. + status(rpcResultBool).withResult(rpcResultType).build()); } @Override diff --git a/opendaylight/md-sal/model/model-inventory/src/main/yang/opendaylight-inventory.yang b/opendaylight/md-sal/model/model-inventory/src/main/yang/opendaylight-inventory.yang index 396206e28e..64d212e9bc 100644 --- a/opendaylight/md-sal/model/model-inventory/src/main/yang/opendaylight-inventory.yang +++ b/opendaylight/md-sal/model/model-inventory/src/main/yang/opendaylight-inventory.yang @@ -9,8 +9,8 @@ module opendaylight-inventory { revision "2013-08-19" { description "Initial revision of Inventory model"; } - - + + typedef support-type { type enumeration { enum native; @@ -21,43 +21,79 @@ module opendaylight-inventory { typedef node-id { type inet:uri; + description "Identifier for a particular node. For example: + + myprotocol: + + myprotocol:12 + + It is a good practice to always lead with a scoping identifier. + In the example above the scoping was 'myprotocol'. In your app you + could use 'myapp' etc."; } typedef node-connector-id { type inet:uri; + description "Identifier for a particular node-connector. For example: + + myprotocol: + myprotocol:3 + + It is a good practice to always lead with a scoping identifier. + In the example above the scoping was 'myprotocol'. In your app you + could use 'myapp' etc."; + } + //YANG does not have a statement which limits the scope of an instance-identifier to a particular subtree, + //which is why we are using a type capture and not an instance-identifier to define a node-ref and a node-connector-ref. typedef node-ref { type instance-identifier; + description "A reference that points to an opendaylight-light:nodes/node in the data tree."; } typedef node-connector-ref { type instance-identifier; + description "A reference that points to an opendaylight-list:nodes/node/{node-id}/node-connector in the data tree."; } identity node-context { - description "Identity used to mark node context"; + description "A node-context is a classifier for node elements which allows an RPC to provide a service on behalf of a particular element in the data tree."; } identity node-connector-context { - description "Identity used to mark node connector context"; + description "A node-connector-context is a classifier for node-connector elements which allows an RPC to provide a service on behalf of a particular element in the data tree."; } + //We are defining a base identity here because there are limitations with yang enums. Yang doesn't allow you to extend enumeratations. + //Therefore by defining a base identity we allow other yang files to extend this identity to define additional "enumerations". By + //using node-type as their base they are able to pass their object to fields that accept "node-types" while uniquely describing their + //type of node, such as "router-node" or "switch-node" etc. + //See https://wiki.opendaylight.org/view/YANG_Tools:YANG_to_Java_Mapping#Identity for more information. identity node-type { - description "Base identity for node types"; + description "A base identity definition which represents a generic node type and can be extended in other yang files."; } identity node-connector-type { - description "Base identity for node connectors type"; + description "A base identity definition which represents a generic node connector type and can be extended in other yang files."; } grouping node { + + description "Describes the contents of a generic node - + essentially an ID and a list of node-connectors. + Acts as an augmentation point where other yang files + can add additional information."; + leaf id { type node-id; + description "The unique identifier for the node."; } list "node-connector" { key "id"; + + description "A list of node connectors that belong this node."; ext:context-instance "node-connector-context"; uses node-connector; @@ -65,55 +101,117 @@ module opendaylight-inventory { } grouping node-connector { + + description "Describes a generic node connector which consists of an ID. + Acts as an augmentation point where other yang files can + add additional information."; + leaf id { type node-connector-id; + description "The unique identifier for the node-connector."; } } grouping node-context-ref { - description - "Helper grouping which contains a reference to node context."; + description + "A helper grouping which contains a reference to a node classified with a node-context. This allows RPCs in other yang files to refine their input to a particular node instance."; + leaf node { ext:context-reference "node-context"; type node-ref; + description "A reference to a particular node."; } } /** Base structure **/ container nodes { + + description "The root container of all nodes."; + list node { key "id"; ext:context-instance "node-context"; - - uses node; + description "A list of nodes (as defined by the 'grouping node')."; + uses node; //this refers to the 'grouping node' defined above. } } + //The following notifications should really be replaced by direct writes to the data tree with data change listeners listening to those changes. + //Notifications should be reserved for one time events which do not require persistence to the data tree. notification node-updated { + + status deprecated; + + description "A notification sent by someone who realized there was a modification to a node, but did not modify the data tree. + Describes that something on the node has been updated (including addition of a new node), but is for + whatever reason is not modifying the data tree. + + Deprecated: If a process determines that a node was updated, then that + logic should update the node using the DataBroker directly. Listeners interested + update changes should register a data change listener for notifications on removals."; + leaf node-ref { ext:context-reference "node-context"; + description "A reference to the node which changed."; + type node-ref; } uses node; } notification node-connector-updated { + + status deprecated; + + description "A notification sent by someone who realized there was a modification to a node-connector, but did not modify the data tree. + Describes that something on the node-connector has been updated (including addition of a new node-connector), but is for + whatever reason is not modifying the data tree. + + Deprecated: If a process determines that a node-connector was updated, then that + logic should update the node-connector using the DataBroker directly. Listeners interested + update changes should register a data change listener for notifications on removals."; + leaf node-connector-ref { ext:context-reference "node-connector-context"; type node-connector-ref; + description "A reference to the node-connector which changed."; } uses node-connector; } notification node-removed { + + status deprecated; + + description "A notification sent by someone who realized there was a node was removed, but did not modify the data tree. + Describes that a node has been removed but is for + whatever reason is not modifying the data tree. + + Deprecated: If a process determines that a node was removed, then that + logic should remove the node from the DataBroker directly. Listeners interested + in changes should register a data change listener for notifications on removals."; + leaf node-ref { + description "A reference to the node that was removed."; ext:context-reference "node-context"; type node-ref; } } notification node-connector-removed { + + status deprecated; + + description "A notification sent by someone who realized there was a node-connector was removed, but did not modify the data tree. + Describes that a node-connector has been removed but is for + whatever reason is not modifying the data tree. + + Deprecated: If a process determines that a node-connector was removed, then that + logic should remove the node-connector from the DataBroker directly. Listeners interested + in changes should register a data change listener for notifications on removals."; + leaf node-connector-ref { + description "A reference to the node-connector that was removed."; ext:context-reference "node-connector-context"; type node-connector-ref; } diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index d3f6d2d005..1cfd5a6639 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -63,9 +63,6 @@ sal-protocolbuffer-encoding - - feature - sal-test-model diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java index a7b5f32957..cc85d4337b 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/ReadTransaction.java @@ -15,7 +15,32 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import com.google.common.base.Optional; import com.google.common.util.concurrent.ListenableFuture; +/** + * A transaction that provides read access to a logical data store. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}. + */ public interface ReadTransaction extends AsyncReadTransaction, DataObject> { - @Override - ListenableFuture> read(LogicalDatastoreType store, InstanceIdentifier path); + + /** + * Reads data from the provided logical data store located at the provided path. + *

+ * If the target is a subtree, then the whole subtree is read (and will be + * accessible from the returned data object). + * + * @param store + * Logical data store from which read should occur. + * @param path + * Path which uniquely identifies subtree which client want to + * read + * @return Listenable Future which contains read result + *

    + *
  • If data at supplied path exists the + * {@link ListeblaFuture#get()} returns Optional object containing + * data once read is done. + *
  • If data at supplied path does not exists the + * {@link ListenbleFuture#get()} returns {@link Optional#absent()}. + *
+ */ + ListenableFuture> read(LogicalDatastoreType store, InstanceIdentifier path); } diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java index 0cef81d359..044a700d84 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/WriteTransaction.java @@ -18,8 +18,46 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. */ public interface WriteTransaction extends AsyncWriteTransaction, DataObject> { - @Override - void put(LogicalDatastoreType store, InstanceIdentifier path, DataObject data); + + /** + * Stores a piece of data at the specified path. This acts as an add / replace + * operation, which is to say that whole subtree will be replaced by the specified data. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + *

+ * If you need to make sure that a parent object exists but you do not want modify + * its pre-existing state by using put, consider using {@link #merge} instead. + * + * @param store + * the logical data store which should be modified + * @param path + * the data object path + * @param data + * the data object to be written to the specified path + * @throws IllegalStateException + * if the transaction has already been submitted + */ + void put(LogicalDatastoreType store, InstanceIdentifier path, T data); + + /** + * Merges a piece of data with the existing data at a specified path. Any pre-existing data + * which is not explicitly overwritten will be preserved. This means that if you store a container, + * its child lists will be merged. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + *

+ * If you require an explicit replace operation, use {@link #put} instead. + * + * @param store + * the logical data store which should be modified + * @param path + * the data object path + * @param data + * the data object to be merged to the specified path + * @throws IllegalStateException + * if the transaction has already been submitted + */ + void merge(LogicalDatastoreType store, InstanceIdentifier path, T data); @Override void delete(LogicalDatastoreType store, InstanceIdentifier path); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java index e5e1e300c1..a6d20c5c34 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedTransaction.java @@ -54,8 +54,8 @@ abstract class AbstractForwardedTransaction> doRead(final DOMDataReadTransaction readTx, - final LogicalDatastoreType store, final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { + protected final ListenableFuture> doRead(final DOMDataReadTransaction readTx, + final LogicalDatastoreType store, final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { return Futures.transform(readTx.read(store, codec.toNormalized(path)), codec.deserializeFunction(path)); } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java index 9eceeb1e43..a8eef5a3ca 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractWriteTransaction.java @@ -10,13 +10,12 @@ package org.opendaylight.controller.md.sal.binding.impl; import java.util.Collections; import java.util.Map.Entry; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.Identifiable; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.slf4j.Logger; @@ -24,7 +23,7 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.collect.Iterables; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.CheckedFuture; /** * @@ -122,8 +121,8 @@ public class AbstractWriteTransaction extends getDelegate().delete(store, normalized); } - protected final ListenableFuture> doCommit() { - return getDelegate().commit(); + protected final CheckedFuture doSubmit() { + return getDelegate().submit(); } protected final boolean doCancel() { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadTransactionImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadTransactionImpl.java index e71404dfa4..bb942047f2 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadTransactionImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadTransactionImpl.java @@ -25,8 +25,8 @@ class BindingDataReadTransactionImpl extends AbstractForwardedTransaction> read(final LogicalDatastoreType store, - final InstanceIdentifier path) { + public ListenableFuture> read(final LogicalDatastoreType store, + final InstanceIdentifier path) { return doRead(getDelegate(),store, path); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadWriteTransactionImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadWriteTransactionImpl.java index 5a89cc70b8..c8b9d9347a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadWriteTransactionImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataReadWriteTransactionImpl.java @@ -25,8 +25,8 @@ class BindingDataReadWriteTransactionImpl extends } @Override - public ListenableFuture> read(final LogicalDatastoreType store, - final InstanceIdentifier path) { + public ListenableFuture> read(final LogicalDatastoreType store, + final InstanceIdentifier path) { return doRead(getDelegate(), store, path); } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataWriteTransactionImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataWriteTransactionImpl.java index a62319be22..29790fb60a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataWriteTransactionImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDataWriteTransactionImpl.java @@ -10,11 +10,13 @@ package org.opendaylight.controller.md.sal.binding.impl; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; - +import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.ListenableFuture; class BindingDataWriteTransactionImpl extends @@ -24,15 +26,15 @@ class BindingDataWriteTransactionImpl extends super(delegateTx, codec); } - - @Override - public void put(final LogicalDatastoreType store, final InstanceIdentifier path, final DataObject data) { + public void put(final LogicalDatastoreType store, final InstanceIdentifier path, + final T data) { doPut(store, path, data); } @Override - public void merge(final LogicalDatastoreType store, final InstanceIdentifier path, final DataObject data) { + public void merge(final LogicalDatastoreType store, final InstanceIdentifier path, + final T data) { doMerge(store, path, data); } @@ -43,7 +45,12 @@ class BindingDataWriteTransactionImpl extends @Override public ListenableFuture> commit() { - return doCommit(); + return AbstractDataTransaction.convertToLegacyCommitFuture(submit()); + } + + @Override + public CheckedFuture submit() { + return doSubmit(); } @Override diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java index d275c838f2..f8c1cf6b99 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingToNormalizedNodeCodec.java @@ -123,7 +123,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { */ public Optional> toBinding( final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) - throws DeserializationException { + throws DeserializationException { PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments()); // Used instance-identifier codec do not support serialization of last @@ -140,7 +140,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { private Optional> toBindingAugmented( final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) - throws DeserializationException { + throws DeserializationException { Optional> potential = toBindingImpl(normalized); // Shorthand check, if codec already supports deserialization // of AugmentationIdentifier we will return @@ -190,7 +190,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { private Optional> toBindingImpl( final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) - throws DeserializationException { + throws DeserializationException { org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath; try { @@ -220,7 +220,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { private DataNormalizationOperation findNormalizationOperation( final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) - throws DataNormalizationException { + throws DataNormalizationException { DataNormalizationOperation current = legacyToNormalized.getRootOperation(); for (PathArgument arg : normalized.getPathArguments()) { current = current.getChild(arg); @@ -264,7 +264,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { public Optional, DataObject>> toBinding( final Entry> normalized) - throws DeserializationException { + throws DeserializationException { Optional> potentialPath = toBinding(normalized.getKey()); if (potentialPath.isPresent()) { InstanceIdentifier bindingPath = potentialPath.get(); @@ -378,7 +378,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { return Optional.absent(); } - private Optional findAugmentation(final Class targetType, + private Optional findAugmentation(final Class targetType, final Set augmentations) { YangModuleInfo moduleInfo; try { @@ -495,6 +495,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { if (isAugmentation(arg.getType())) { count++; } + } return count; } @@ -509,12 +510,12 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { return count; } - public Function>, Optional> deserializeFunction( - final InstanceIdentifier path) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Function>, Optional> deserializeFunction(final InstanceIdentifier path) { return new DeserializeFunction(this, path); } - private static class DeserializeFunction implements Function>, Optional> { + private static class DeserializeFunction implements Function>, Optional> { private final BindingToNormalizedNodeCodec codec; private final InstanceIdentifier path; @@ -525,9 +526,10 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { this.path = Preconditions.checkNotNull(path, "Path must not be null"); } + @SuppressWarnings("rawtypes") @Nullable @Override - public Optional apply(@Nullable final Optional> normalizedNode) { + public Optional apply(@Nullable final Optional> normalizedNode) { if (normalizedNode.isPresent()) { final DataObject dataObject; try { @@ -548,8 +550,7 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { /** * Returns an default object according to YANG schema for supplied path. * - * @param path - * DOM Path + * @param path DOM Path * @return Node with defaults set on. */ public NormalizedNode getDefaultNodeFor(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path) { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java index 1c6447a4e7..12f26b09bb 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java @@ -28,12 +28,12 @@ import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.Data import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration; import org.opendaylight.controller.md.sal.common.api.data.DataReader; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; import org.opendaylight.controller.sal.binding.api.data.DataChangeListener; import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; import org.opendaylight.controller.sal.binding.api.data.DataProviderService; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; import org.opendaylight.yangtools.concepts.Delegator; @@ -42,8 +42,8 @@ import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.concepts.util.ListenerRegistry; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -161,9 +161,9 @@ public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDat @Override public ListenableFuture> apply(final Boolean requestCommitSuccess) throws Exception { if(requestCommitSuccess) { - return tx.getDelegate().commit(); + return AbstractDataTransaction.convertToLegacyCommitFuture(tx.getDelegate().submit()); } - return Futures.immediateFuture(Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.emptySet())); + return Futures.immediateFuture(RpcResultBuilder.failed().withResult(TransactionStatus.FAILED).build()); } }); 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 index 052fd2169a..709b62fee2 100644 --- 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 @@ -37,8 +37,6 @@ RpcRouter, RouteChangeListener, InstanceIdentif private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class); - private T defaultService; - private final Class serviceType; private final T invocationProxy; @@ -49,11 +47,8 @@ RpcRouter, RouteChangeListener, InstanceIdentif private final Map, RpcRoutingTableImpl> routingTables; - private final String name; - @SuppressWarnings("unchecked") public RpcRouterCodegenInstance(final String name,final Class type, final T routerImpl, final Iterable> contexts) { - this.name = name; this.listeners = ListenerRegistry.create(); this.serviceType = type; this.invocationProxy = routerImpl; @@ -90,7 +85,7 @@ RpcRouter, RouteChangeListener, InstanceIdentif @Override public T getDefaultService() { - return defaultService; + return RuntimeCodeHelper.getDelegate(invocationProxy); } @Override @@ -125,11 +120,17 @@ RpcRouter, RouteChangeListener, InstanceIdentif return new RoutedRpcRegistrationImpl(service); } + public void removeDefaultImplementation(final T instance) { + RpcService current = RuntimeCodeHelper.getDelegate(invocationProxy); + if(instance == current) { + RuntimeCodeHelper.setDelegate(invocationProxy, null); + } + } + @Override public RpcRegistration registerDefaultService(final T service) { - // TODO Auto-generated method stub RuntimeCodeHelper.setDelegate(invocationProxy, service); - return null; + return new DefaultRpcImplementationRegistration(service); } private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration implements RoutedRpcRegistration { @@ -168,4 +169,24 @@ RpcRouter, RouteChangeListener, InstanceIdentif } } + + private class DefaultRpcImplementationRegistration extends AbstractObjectRegistration implements RpcRegistration { + + + protected DefaultRpcImplementationRegistration(final T instance) { + super(instance); + } + + @Override + protected void removeRegistration() { + removeDefaultImplementation(this.getInstance()); + } + + @Override + public Class getServiceType() { + return serviceType; + } + } + + } 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 index 542dfa7e7b..c61ec4926a 100644 --- 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 @@ -7,6 +7,19 @@ */ package org.opendaylight.controller.sal.binding.impl; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.EventListener; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.WeakHashMap; + 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; @@ -28,23 +41,21 @@ import org.opendaylight.yangtools.yang.binding.RpcService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.EventListener; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.WeakHashMap; - -import static com.google.common.base.Preconditions.checkState; - -public class RpcProviderRegistryImpl implements // - RpcProviderRegistry, // - RouteChangePublisher> { +public class RpcProviderRegistryImpl implements RpcProviderRegistry, RouteChangePublisher> { private RuntimeCodeGenerator rpcFactory = SingletonHolder.RPC_GENERATOR_IMPL; - // publicProxies is a cache of proxy objects where each value in the map corresponds to a specific RpcService - private final Map, RpcService> publicProxies = new WeakHashMap<>(); + // cache of proxy objects where each value in the map corresponds to a specific RpcService + private final LoadingCache, RpcService> publicProxies = CacheBuilder.newBuilder().weakKeys(). + build(new CacheLoader, RpcService>() { + @Override + public RpcService load(final Class type) { + final RpcService proxy = rpcFactory.getDirectProxyFor(type); + LOG.debug("Created {} as public proxy for {} in {}", proxy, type.getSimpleName(), this); + return proxy; + } + }); + private final Map, RpcRouter> rpcRouters = new WeakHashMap<>(); private final ListenerRegistry>> routeChangeListeners = ListenerRegistry .create(); @@ -60,19 +71,19 @@ public class RpcProviderRegistryImpl implements // return name; } - public RpcProviderRegistryImpl(String name) { + public RpcProviderRegistryImpl(final String name) { super(); this.name = name; } @Override - public final RoutedRpcRegistration addRoutedRpcImplementation(Class type, - T implementation) throws IllegalStateException { + public final RoutedRpcRegistration addRoutedRpcImplementation(final Class type, + final T implementation) throws IllegalStateException { return getRpcRouter(type).addRoutedRpcImplementation(implementation); } @Override - public final RpcRegistration addRpcImplementation(Class type, T implementation) + public final RpcRegistration addRpcImplementation(final Class type, final T implementation) throws IllegalStateException { @SuppressWarnings("unchecked") RpcRouter potentialRouter = (RpcRouter) rpcRouters.get(type); @@ -92,31 +103,12 @@ public class RpcProviderRegistryImpl implements // @SuppressWarnings("unchecked") @Override - public final T getRpcService(Class type) { - - T potentialProxy = (T) publicProxies.get(type); - if (potentialProxy != null) { - return potentialProxy; - } - synchronized (this) { - /** - * Potential proxy could be instantiated by other thread while we - * were waiting for the lock. - */ - - potentialProxy = (T) publicProxies.get(type); - if (potentialProxy != null) { - return potentialProxy; - } - T proxy = rpcFactory.getDirectProxyFor(type); - LOG.debug("Created {} as public proxy for {} in {}", proxy, type.getSimpleName(), this); - publicProxies.put(type, proxy); - return proxy; - } + public final T getRpcService(final Class type) { + return (T) publicProxies.getUnchecked(type); } - @SuppressWarnings("unchecked") - public RpcRouter getRpcRouter(Class type) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + public RpcRouter getRpcRouter(final Class type) { RpcRouter potentialRouter = rpcRouters.get(type); if (potentialRouter != null) { return (RpcRouter) potentialRouter; @@ -140,7 +132,7 @@ public class RpcProviderRegistryImpl implements // } } - private void notifyGlobalRpcAdded(Class type) { + private void notifyGlobalRpcAdded(final Class type) { for(ListenerRegistration listener : globalRpcListeners) { try { listener.getInstance().onGlobalRpcRegistered(type); @@ -151,7 +143,7 @@ public class RpcProviderRegistryImpl implements // } - private void notifyListenersRoutedCreated(RpcRouter router) { + private void notifyListenersRoutedCreated(final RpcRouter router) { for (ListenerRegistration listener : routerInstantiationListener) { try { @@ -164,7 +156,7 @@ public class RpcProviderRegistryImpl implements // } public ListenerRegistration registerRouterInstantiationListener( - RouterInstantiationListener listener) { + final RouterInstantiationListener listener) { ListenerRegistration reg = routerInstantiationListener.register(listener); try { for (RpcRouter router : rpcRouters.values()) { @@ -176,9 +168,10 @@ public class RpcProviderRegistryImpl implements // return reg; } + @SuppressWarnings("unchecked") @Override public >> ListenerRegistration registerRouteChangeListener( - L listener) { + final L listener) { return (ListenerRegistration) routeChangeListeners.register(listener); } @@ -186,7 +179,7 @@ public class RpcProviderRegistryImpl implements // return rpcFactory; } - public void setRpcFactory(RuntimeCodeGenerator rpcFactory) { + public void setRpcFactory(final RuntimeCodeGenerator rpcFactory) { this.rpcFactory = rpcFactory; } @@ -194,7 +187,7 @@ public class RpcProviderRegistryImpl implements // void onRpcRouterCreated(RpcRouter router); } - public ListenerRegistration registerGlobalRpcRegistrationListener(GlobalRpcRegistrationListener listener) { + public ListenerRegistration registerGlobalRpcRegistrationListener(final GlobalRpcRegistrationListener listener) { return globalRpcListeners.register(listener); } @@ -204,17 +197,16 @@ public class RpcProviderRegistryImpl implements // } - private class RouteChangeForwarder implements - RouteChangeListener, InstanceIdentifier> { + private class RouteChangeForwarder implements RouteChangeListener, InstanceIdentifier> { private final Class type; - public RouteChangeForwarder(Class type) { + public RouteChangeForwarder(final Class type) { this.type = type; } @Override - public void onRouteChange(RouteChange, InstanceIdentifier> change) { + public void onRouteChange(final RouteChange, InstanceIdentifier> change) { Map>> announcements = new HashMap<>(); for (Entry, Set>> entry : change.getAnnouncements() .entrySet()) { @@ -239,13 +231,12 @@ public class RpcProviderRegistryImpl implements // } } - public static class RpcProxyRegistration extends AbstractObjectRegistration implements - RpcRegistration { + public static class RpcProxyRegistration extends AbstractObjectRegistration implements RpcRegistration { private final Class serviceType; private RpcProviderRegistryImpl registry; - public RpcProxyRegistration(Class type, T service, RpcProviderRegistryImpl registry) { + public RpcProxyRegistration(final Class type, final T service, final RpcProviderRegistryImpl registry) { super(service); this.serviceType = type; this.registry = registry; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomCommitHandler.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomCommitHandler.java index ee0628308e..7434309387 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomCommitHandler.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomCommitHandler.java @@ -2,6 +2,7 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; import java.util.Map; import java.util.concurrent.ConcurrentMap; + import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; import org.opendaylight.controller.md.sal.common.api.data.DataModification; import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions; @@ -14,6 +15,10 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @deprecated This is part of the legacy DataBrokerService + */ +@Deprecated class BindingToDomCommitHandler implements DataCommitHandler, DataObject> { @@ -34,7 +39,7 @@ class BindingToDomCommitHandler implements this.biDataService = biDataService; } - public void setMappingService(BindingIndependentMappingService mappingService) { + public void setMappingService(final BindingIndependentMappingService mappingService) { this.mappingService = mappingService; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomTransaction.java index 20a786e774..081adb44dc 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomTransaction.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingToDomTransaction.java @@ -1,18 +1,17 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; -import java.util.Collections; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; + 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.DataModification; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; class BindingToDomTransaction implements DataCommitHandler.DataCommitTransaction, DataObject> { @@ -41,7 +40,8 @@ class BindingToDomTransaction implements try { RpcResult biResult = result.get(); domOpenedTransactions.remove(backing.getIdentifier()); - return Rpcs.getRpcResult(biResult.isSuccessful(), null, biResult.getErrors()); + return RpcResultBuilder. status(biResult.isSuccessful()) + .withRpcErrors(biResult.getErrors()).build(); } catch (InterruptedException e) { throw new IllegalStateException("", e); } catch (ExecutionException e) { @@ -54,6 +54,6 @@ class BindingToDomTransaction implements @Override public RpcResult rollback() throws IllegalStateException { domOpenedTransactions.remove(backing.getIdentifier()); - return Rpcs.getRpcResult(true, null, Collections. emptySet()); + return RpcResultBuilder. success().build(); } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingCommitHandler.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingCommitHandler.java index 395af8f487..88024b9af2 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingCommitHandler.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingCommitHandler.java @@ -2,6 +2,7 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; import java.util.Map; import java.util.concurrent.ConcurrentMap; + import org.opendaylight.controller.md.sal.common.api.RegistrationListener; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration; @@ -16,6 +17,10 @@ import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * @deprecated This is part of the legacy DataBrokerService + */ +@Deprecated class DomToBindingCommitHandler implements // RegistrationListener, DataObject>>, // DataCommitHandler { @@ -34,11 +39,11 @@ class DomToBindingCommitHandler implements // private DataProviderService baDataService; private BindingIndependentMappingService mappingService; - public void setBindingAwareDataService(DataProviderService baDataService) { + public void setBindingAwareDataService(final DataProviderService baDataService) { this.baDataService = baDataService; } - public void setMappingService(BindingIndependentMappingService mappingService) { + public void setMappingService(final BindingIndependentMappingService mappingService) { this.mappingService = mappingService; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingTransaction.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingTransaction.java index 45be5c96fa..cb271a1a91 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingTransaction.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/DomToBindingTransaction.java @@ -1,15 +1,14 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; -import java.util.Collections; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; + 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.DataModification; -import org.opendaylight.controller.sal.common.util.Rpcs; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; @@ -39,7 +38,7 @@ class DomToBindingTransaction implements @Override public RpcResult rollback() throws IllegalStateException { bindingOpenedTransactions.remove(backing.getIdentifier()); - return Rpcs.getRpcResult(true, null, Collections.emptySet()); + return RpcResultBuilder. success().build(); } @Override @@ -48,7 +47,8 @@ class DomToBindingTransaction implements try { RpcResult baResult = result.get(); bindingOpenedTransactions.remove(backing.getIdentifier()); - return Rpcs.getRpcResult(baResult.isSuccessful(), null, baResult.getErrors()); + return RpcResultBuilder. status(baResult.isSuccessful()) + .withRpcErrors(baResult.getErrors()).build(); } catch (InterruptedException e) { throw new IllegalStateException("", e); } catch (ExecutionException e) { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategy.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategy.java index d08b217e71..f03d07eb99 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategy.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategy.java @@ -10,19 +10,17 @@ package org.opendaylight.controller.sal.binding.impl.connect.dom; import java.lang.ref.WeakReference; import java.lang.reflect.Method; -import java.util.Collection; import java.util.Collections; import java.util.concurrent.Future; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; @@ -89,7 +87,7 @@ public class RpcInvocationStrategy { public ListenableFuture> forwardToDomBroker(final DataObject input) { if(biRpcRegistry == null) { - return Futures.> immediateFuture(Rpcs.getRpcResult(false)); + return Futures.> immediateFuture(RpcResultBuilder.failed().build()); } CompositeNode inputXml = null; @@ -102,6 +100,7 @@ public class RpcInvocationStrategy { Function, RpcResult> transformationFunction = new Function, RpcResult>() { + @SuppressWarnings("rawtypes") @Override public RpcResult apply(RpcResult result) { @@ -114,7 +113,7 @@ public class RpcInvocationStrategy { } } - return Rpcs.getRpcResult(result.isSuccessful(), output, result.getErrors()); + return RpcResultBuilder.from( (RpcResult)result ).withResult( output ).build(); } }; @@ -135,22 +134,18 @@ public class RpcInvocationStrategy { } if (futureResult == null) { - return Rpcs.getRpcResult(false); + return RpcResultBuilder.failed().build(); } - RpcResult bindingResult = futureResult.get(); - - Collection errors = bindingResult.getErrors(); - if( errors == null ) { - errors = Collections.emptySet(); - } + @SuppressWarnings("rawtypes") + RpcResult bindingResult = futureResult.get(); final Object resultObj = bindingResult.getResult(); - CompositeNode output = null; + Object output = null; if (resultObj instanceof DataObject) { output = mappingService.toDataDom((DataObject)resultObj); } - return Rpcs.getRpcResult( bindingResult.isSuccessful(), output, errors); + return RpcResultBuilder.from( bindingResult ).withResult( output ).build(); } public RpcResult invokeOn(final RpcService rpcService, final CompositeNode domInput) throws Exception { diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/MapUtils.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/MapUtils.java deleted file mode 100644 index aa6a9a21e5..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/util/MapUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2014 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.sal.binding.impl.util; - -import com.google.common.collect.Multimap; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map.Entry; -import org.opendaylight.yangtools.concepts.Path; - -@SuppressWarnings("all") -public class MapUtils { - public static

, V extends Object> Collection> getAllChildren(final Multimap map, final P path) { - HashSet> _hashSet = new HashSet>(); - final HashSet> ret = _hashSet; - final Collection> entries = map.entries(); - for (final Entry entry : entries) { - { - final P currentPath = entry.getKey(); - if (path.contains(currentPath)) { - ret.add(entry); - } else if (currentPath.contains(path)){ - ret.add(entry); - } - } - } - return ret; - } -} - diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java index 9f1ae38457..fb11535061 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1125RegressionTest.java @@ -49,13 +49,13 @@ public class Bug1125RegressionTest extends AbstractDataChangeListenerTest { "foo").build()).build(); initialTx.put(LogicalDatastoreType.OPERATIONAL, path(TOP_FOO_KEY), topLevelList(TOP_FOO_KEY, fooAugment)); - assertCommit(initialTx.commit()); + assertCommit(initialTx.submit()); } private void delete(final InstanceIdentifier path) { WriteTransaction tx = getDataBroker().newWriteOnlyTransaction(); tx.delete(LogicalDatastoreType.OPERATIONAL, path); - assertCommit(tx.commit()); + assertCommit(tx.submit()); } private void verifyRemoved( diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java index 05bc857969..54493300e3 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/ListInsertionDataChangeListenerTest.java @@ -48,7 +48,7 @@ public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListe protected void setupWithDataBroker(final DataBroker dataBroker) { WriteTransaction initialTx = dataBroker.newWriteOnlyTransaction(); initialTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_FOO_KEY))); - assertCommit(initialTx.commit()); + assertCommit(initialTx.submit()); } @Test @@ -60,7 +60,7 @@ public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListe ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction(); writeTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY))); - assertCommit(writeTx.commit()); + assertCommit(writeTx.submit()); AsyncDataChangeEvent, DataObject> top = topListener.event(); AsyncDataChangeEvent, DataObject> all = allListener.event(); AsyncDataChangeEvent, DataObject> foo = fooListener.event(); @@ -115,7 +115,7 @@ public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListe ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction(); writeTx.merge(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY))); - assertCommit(writeTx.commit()); + assertCommit(writeTx.submit()); verifyBarOnlyAdded(topListener,allListener,fooListener,barListener); } @@ -129,7 +129,7 @@ public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListe ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction(); writeTx.put(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY)); - assertCommit(writeTx.commit()); + assertCommit(writeTx.submit()); verifyBarOnlyAdded(topListener,allListener,fooListener,barListener); } @@ -143,7 +143,7 @@ public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListe ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction(); writeTx.merge(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY)); - assertCommit(writeTx.commit()); + assertCommit(writeTx.submit()); verifyBarOnlyAdded(topListener,allListener,fooListener,barListener); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/RpcProviderRegistryTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/RpcProviderRegistryTest.java new file mode 100644 index 0000000000..110e5b4dce --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/RpcProviderRegistryTest.java @@ -0,0 +1,137 @@ +package org.opendaylight.controller.md.sal.binding.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path; + +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.md.sal.binding.test.AssertCollections; +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.BindingAwareBroker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; +import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier; +import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter; +import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.OpendaylightTestRoutedRpcService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.TestContext; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import com.google.common.util.concurrent.SettableFuture; + + +public class RpcProviderRegistryTest { + + private static InstanceIdentifier FOO_PATH = path(TOP_FOO_KEY); + private static InstanceIdentifier BAR_PATH = path(TOP_BAR_KEY); + private static RpcContextIdentifier ROUTING_CONTEXT = RpcContextIdentifier.contextFor(OpendaylightTestRoutedRpcService.class, TestContext.class); + + private RpcProviderRegistryImpl rpcRegistry; + + @Before + public void setup() { + rpcRegistry = new RpcProviderRegistryImpl("test"); + } + + private static class TestListener implements RouteChangeListener> { + + final SettableFuture>> event = SettableFuture.create(); + @Override + public void onRouteChange( + final RouteChange> change) { + event.set(change); + } + } + + @Test + public void testGlobalRpcRegistrations() throws Exception { + OpendaylightTestRpcServiceService one = Mockito.mock(OpendaylightTestRpcServiceService.class); + OpendaylightTestRpcServiceService two = Mockito.mock(OpendaylightTestRpcServiceService.class); + + RpcRegistration regOne = rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, one); + assertNotNull(regOne); + + try { + rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, two); + fail("Second call for registration of same RPC must throw IllegalStateException"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + regOne.close(); + + RpcRegistration regTwo = rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, two); + assertNotNull(regTwo); + } + + @Test + public void testRpcRouterInstance() throws Exception { + OpendaylightTestRoutedRpcService def = Mockito.mock(OpendaylightTestRoutedRpcService.class); + + RpcRouter router = rpcRegistry.getRpcRouter(OpendaylightTestRoutedRpcService.class); + + assertEquals(OpendaylightTestRoutedRpcService.class, router.getServiceType()); + assertNotNull(router.getInvocationProxy()); + assertNull(router.getDefaultService()); + + AssertCollections.assertContains(router.getContexts(), TestContext.class); + + RpcRegistration regDef = router.registerDefaultService(def); + assertNotNull(regDef); + assertEquals(OpendaylightTestRoutedRpcService.class,regDef.getServiceType()); + assertEquals(def,regDef.getInstance()); + assertEquals(def, router.getDefaultService()); + + regDef.close(); + assertNull("Default instance should be null after closing registration", router.getDefaultService()); + } + + @Test + public void testRoutedRpcPathChangeEvents() throws InterruptedException, TimeoutException, ExecutionException { + OpendaylightTestRoutedRpcService one = Mockito.mock(OpendaylightTestRoutedRpcService.class); + OpendaylightTestRoutedRpcService two = Mockito.mock(OpendaylightTestRoutedRpcService.class); + RoutedRpcRegistration regOne = rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRoutedRpcService.class, one); + RoutedRpcRegistration regTwo = rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRoutedRpcService.class, two); + assertNotNull(regOne); + assertNotNull(regTwo); + + final TestListener addListener = new TestListener(); + rpcRegistry.registerRouteChangeListener(addListener); + regOne.registerPath(TestContext.class, FOO_PATH); + + RouteChange> fooAddEvent = addListener.event.get(500, TimeUnit.MILLISECONDS); + Set> announce = fooAddEvent.getAnnouncements().get(ROUTING_CONTEXT); + assertNotNull(announce); + AssertCollections.assertContains(announce, FOO_PATH); + AssertCollections.assertNotContains(announce, BAR_PATH); + + + + final TestListener removeListener = new TestListener(); + rpcRegistry.registerRouteChangeListener(removeListener); + + regOne.unregisterPath(TestContext.class, FOO_PATH); + + RouteChange> fooRemoveEvent = removeListener.event.get(500, TimeUnit.MILLISECONDS); + Set> removal = fooRemoveEvent.getRemovals().get(ROUTING_CONTEXT); + assertNotNull(removal); + AssertCollections.assertContains(removal, FOO_PATH); + AssertCollections.assertNotContains(removal, BAR_PATH); + + + } + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java index 43e951423c..b577e2af1f 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/WriteTransactionTest.java @@ -7,14 +7,11 @@ */ package org.opendaylight.controller.md.sal.binding.impl.test; -import static org.junit.Assert.assertEquals; - import java.util.concurrent.ExecutionException; import org.junit.Test; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder; @@ -35,7 +32,7 @@ public class WriteTransactionTest extends AbstractDataBrokerTest { WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction(); writeTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH, new TopBuilder().build()); writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, new TopLevelListBuilder().setKey(TOP_LIST_KEY).build()); - assertEquals(TransactionStatus.COMMITED, writeTx.commit().get().getResult()); + writeTx.submit().get(); } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java index 7f23ac26b6..5789270dee 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/AbstractDataBrokerTest.java @@ -7,16 +7,12 @@ */ package org.opendaylight.controller.md.sal.binding.test; -import static org.junit.Assert.assertEquals; - import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opendaylight.controller.md.sal.binding.api.DataBroker; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import com.google.common.util.concurrent.ListenableFuture; @@ -53,9 +49,9 @@ public class AbstractDataBrokerTest extends AbstractSchemaAwareTest { return domBroker; } - protected static final void assertCommit(final ListenableFuture> commit) { + protected static final void assertCommit(final ListenableFuture commit) { try { - assertEquals(TransactionStatus.COMMITED,commit.get(500, TimeUnit.MILLISECONDS).getResult()); + commit.get(500, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new IllegalStateException(e); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategyTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategyTest.java index c5aea8f2ab..6977588a01 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategyTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/impl/connect/dom/RpcInvocationStrategyTest.java @@ -25,13 +25,13 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.RpcService; 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; @@ -96,9 +96,11 @@ public class RpcInvocationStrategyTest { private void setupForForwardToDom(boolean hasOutput, boolean hasInput, int expectedErrorSize) { - if (expectedErrorSize > 0) + if (expectedErrorSize > 0) { errors.add(rpcError); - RpcResult result = Rpcs.getRpcResult(true, invokeRpcResult, errors); + } + RpcResult result = RpcResultBuilder.success(invokeRpcResult) + .withRpcErrors( errors ).build(); futureCompNode = Futures.immediateFuture(result); if( hasInput ) { @@ -191,22 +193,28 @@ public class RpcInvocationStrategyTest { * invokeOn Tests */ private void setupRpcResultsWithOutput(int expectedErrorSize) { - if (expectedErrorSize > 0) + if (expectedErrorSize > 0) { errors.add(rpcError); - RpcResult resultCompNode = Rpcs.getRpcResult(true, inputInvokeOn, errors); + } + RpcResult resultCompNode = RpcResultBuilder.success(inputInvokeOn) + .withRpcErrors(errors).build(); futureCompNode = Futures.immediateFuture(resultCompNode); - RpcResult resultDataObj = Rpcs.getRpcResult(true, toDataDomInput, errors); + RpcResult resultDataObj = RpcResultBuilder.success(toDataDomInput) + .withRpcErrors(errors).build(); futureDataObj = Futures.immediateFuture(resultDataObj); when(mockMappingService.toDataDom(toDataDomInput)).thenReturn(outputInvokeOn); } private void setupRpcResultsNoOutput(int expectedErrorSize) { - if (expectedErrorSize > 0) + if (expectedErrorSize > 0) { errors.add(rpcError); - RpcResult resultCompNode = Rpcs.getRpcResult(true, inputInvokeOn, errors); + } + RpcResult resultCompNode = RpcResultBuilder.success(inputInvokeOn) + .withRpcErrors(errors).build(); futureCompNode = Futures.immediateFuture(resultCompNode); - RpcResult resultDataObj = Rpcs.getRpcResult(true, null, errors); + RpcResult resultDataObj = RpcResultBuilder.success() + .withRpcErrors(errors).build(); futureDataObj = Futures.immediateFuture(resultDataObj); } 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 index ca38ed0797..48e1db0e63 100644 --- 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 @@ -23,7 +23,6 @@ import org.junit.Test; 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; @@ -42,12 +41,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -136,7 +134,7 @@ public class CrossBrokerRpcTest { @Override public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { CompositeNode result = testContext.getBindingToDomMappingService().toDataDom(output); - return Futures.immediateFuture(Rpcs.getRpcResult(true, result, ImmutableList.of())); + return Futures.immediateFuture(RpcResultBuilder.success(result).build()); } }); registration.registerPath(NodeContext.QNAME, BI_NODE_C_ID); @@ -168,7 +166,7 @@ public class CrossBrokerRpcTest { 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()); + RpcResult result = RpcResultBuilder.status(success).withResult(output).build(); return Futures.immediateFuture(result); } diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java index c87b93625f..0c123ebe66 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/connect/dom/DOMRpcServiceTestBugfix560.java @@ -25,7 +25,6 @@ import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance; import org.opendaylight.controller.sal.binding.api.mount.MountProviderService; 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.mount.MountProvisionInstance; import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; @@ -39,6 +38,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -135,8 +135,8 @@ public class DOMRpcServiceTestBugfix560 { public ListenableFuture> invokeRpc( final QName rpc, final CompositeNode input) { - return Futures.immediateFuture(Rpcs - . getRpcResult(true)); + return Futures.immediateFuture(RpcResultBuilder + . success().build()); } @Override diff --git a/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java b/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java index 3df33ba377..49ac59fe95 100644 --- a/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java +++ b/opendaylight/md-sal/sal-binding-util/src/main/java/org/opendaylight/controller/md/sal/binding/util/TypeSafeDataReader.java @@ -11,6 +11,14 @@ import org.opendaylight.controller.md.sal.common.api.data.DataReader; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * + * + * @deprecated Use + * {@link org.opendaylight.controller.md.sal.binding.api.ReadTransaction#read(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier)} + * instead. + */ +@Deprecated public final class TypeSafeDataReader { private final DataReader, DataObject> delegate; @@ -19,21 +27,25 @@ public final class TypeSafeDataReader { return delegate; } - public TypeSafeDataReader(DataReader, DataObject> delegate) { + public TypeSafeDataReader( + final DataReader, DataObject> delegate) { this.delegate = delegate; } @SuppressWarnings("unchecked") - public D readConfigurationData(InstanceIdentifier path) { + public D readConfigurationData( + final InstanceIdentifier path) { return (D) delegate.readConfigurationData(path); } @SuppressWarnings("unchecked") - public D readOperationalData(InstanceIdentifier path) { + public D readOperationalData( + final InstanceIdentifier path) { return (D) delegate.readOperationalData(path); } - public static TypeSafeDataReader forReader(DataReader, DataObject> delegate) { + public static TypeSafeDataReader forReader( + final DataReader, DataObject> delegate) { return new TypeSafeDataReader(delegate); } } diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java index 4beb5c62e3..46c90b9756 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadOnlyTransaction.java @@ -10,12 +10,11 @@ package org.opendaylight.controller.md.sal.common.api.data; import org.opendaylight.yangtools.concepts.Path; /** - * Read-only transaction, which provides stable view of data - * and is {@link AutoCloseable} resource. + * Marker interface for a read-only view of the data tree. * * @see AsyncReadTransaction * -* @param

+ * @param

* Type of path (subtree identifier), which represents location in * tree * @param diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java index e1cd4a712a..afa86704ff 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java @@ -9,12 +9,9 @@ package org.opendaylight.controller.md.sal.common.api.data; import org.opendaylight.yangtools.concepts.Path; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.ListenableFuture; - /** * - * Provides a stateful read view of the data tree. + * Marker interface for stateful read view of the data tree. * *

* View of the data tree is a stable point-in-time snapshot of the current data tree state when @@ -47,9 +44,9 @@ import com.google.common.util.concurrent.ListenableFuture; *

* Note: example contains blocking calls on future only to illustrate * that action happened after other asynchronous action. Use of blocking call - * {@link ListenableFuture#get()} is discouraged for most uses and you should - * use - * {@link com.google.common.util.concurrent.Futures#addCallback(ListenableFuture, com.google.common.util.concurrent.FutureCallback)} + * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most + * uses and you should use + * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)} * or other functions from {@link com.google.common.util.concurrent.Futures} to * register more specific listeners. * @@ -58,30 +55,10 @@ import com.google.common.util.concurrent.ListenableFuture; * tree * @param * Type of data (payload), which represents data payload + * + * @see org.opendaylight.controller.md.sal.binding.api.ReadTransaction + * @see org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction */ public interface AsyncReadTransaction

, D> extends AsyncTransaction { - /** - * - * Reads data from provided logical data store located at the provided path. - *

- * If the target is a subtree, then the whole subtree is read (and will be - * accessible from the returned data object). - * - * @param store - * Logical data store from which read should occur. - * @param path - * Path which uniquely identifies subtree which client want to - * read - * @return Listenable Future which contains read result - *

    - *
  • If data at supplied path exists the - * {@link ListeblaFuture#get()} returns Optional object containing - * data once read is done. - *
  • If data at supplied path does not exists the - * {@link ListenbleFuture#get()} returns {@link Optional#absent()}. - *
- */ - ListenableFuture> read(LogicalDatastoreType store, P path); - } diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java index f7eae27320..e47b54a0a1 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java @@ -11,6 +11,7 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.yangtools.concepts.Path; import org.opendaylight.yangtools.yang.common.RpcResult; +import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.ListenableFuture; /** @@ -26,15 +27,57 @@ import com.google.common.util.concurrent.ListenableFuture; * change for the data tree and it is not visible to any other concurrently running * transaction. *

- * Applications publish the changes proposed in the transaction by calling {@link #commit} - * on the transaction. This seals the transaction + * Applications make changes to the local data tree in the transaction by via the + * put, merge, and delete operations. + * + *

Put operation

+ * Stores a piece of data at a specified path. This acts as an add / replace + * operation, which is to say that whole subtree will be replaced by the + * specified data. + *

+ * Performing the following put operations: + * + *

+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * 
+ * + * will result in the following data being present: + * + *
+ * container { list [ b ] }
+ * 
+ *

Merge operation

+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data + * which is not explicitly overwritten will be preserved. This means that if you store a container, + * its child lists will be merged. + *

+ * Performing the following merge operations: + * + *

+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * 
+ * + * will result in the following data being present: + * + *
+ * container { list [ a, b ] }
+ * 
+ * + * This also means that storing the container will preserve any + * augmentations which have been attached to it. + * + *

Delete operation

+ * Removes a piece of data from a specified path. + *

+ * After applying changes to the local data tree, applications publish the changes proposed in the + * transaction by calling {@link #submit} on the transaction. This seals the transaction * (preventing any further writes using this transaction) and submits it to be * processed and applied to global conceptual data tree. *

* The transaction commit may fail due to a concurrent transaction modifying and committing data in - * an incompatible way. See {@link #commit()} for more concrete commit failure examples. - * - * + * an incompatible way. See {@link #submit} for more concrete commit failure examples. *

* Implementation Note: This interface is not intended to be implemented * by users of MD-SAL, but only to be consumed by them. @@ -56,7 +99,7 @@ public interface AsyncWriteTransaction

, D> extends AsyncTransa * {@link TransactionStatus#CANCELED} will have no effect, and transaction * is considered cancelled. * - * Invoking cancel() on finished transaction (future returned by {@link #commit()} + * Invoking cancel() on finished transaction (future returned by {@link #submit()} * already completed with {@link TransactionStatus#COMMITED}) will always * fail (return false). * @@ -65,77 +108,10 @@ public interface AsyncWriteTransaction

, D> extends AsyncTransa * true otherwise * */ - public boolean cancel(); - - /** - * Store a piece of data at specified path. This acts as an add / replace - * operation, which is to say that whole subtree will be replaced by - * specified path. Performing the following put operations: - * - *

-     * 1) container { list [ a ] }
-     * 2) container { list [ b ] }
-     * 
- * - * will result in the following data being present: - * - *
-     * container { list [ b ] }
-     * 
- * - * - * If you need to make sure that a parent object exists, but you do not want modify - * its preexisting state by using put, consider using - * {@link #merge(LogicalDatastoreType, Path, Object)} - * - * @param store - * Logical data store which should be modified - * @param path - * Data object path - * @param data - * Data object to be written to specified path - * @throws IllegalStateException - * if the transaction is no longer {@link TransactionStatus#NEW} - */ - public void put(LogicalDatastoreType store, P path, D data); - - /** - * Store a piece of data at the specified path. This acts as a merge operation, - * which is to say that any pre-existing data which is not explicitly - * overwritten will be preserved. This means that if you store a container, - * its child lists will be merged. Performing the following merge - * operations: - * - *
-     * 1) container { list [ a ] }
-     * 2) container { list [ b ] }
-     * 
- * - * will result in the following data being present: - * - *
-     * container { list [ a, b ] }
-     * 
- * - * This also means that storing the container will preserve any - * augmentations which have been attached to it. - *

- * If you require an explicit replace operation, use - * {@link #put(LogicalDatastoreType, Path, Object)} instead. - * - * @param store - * Logical data store which should be modified - * @param path - * Data object path - * @param data - * Data object to be written to specified path - * @throws IllegalStateException - * if the transaction is no longer {@link TransactionStatus#NEW} - */ - public void merge(LogicalDatastoreType store, P path, D data); + boolean cancel(); /** - * Remove a piece of data from specified path. This operation does not fail + * Removes a piece of data from specified path. This operation does not fail * if the specified path does not exist. * * @param store @@ -145,10 +121,14 @@ public interface AsyncWriteTransaction

, D> extends AsyncTransa * @throws IllegalStateException * if the transaction is no longer {@link TransactionStatus#NEW} */ - public void delete(LogicalDatastoreType store, P path); + void delete(LogicalDatastoreType store, P path); /** - * Submits transaction to be applied to update logical data tree. + * Submits this transaction to be asynchronously applied to update the logical data tree. + * The returned CheckedFuture conveys the result of applying the data changes. + *

+ * Note: It is strongly recommended to process the CheckedFuture result in an asynchronous + * manner rather than using the blocking get() method. See example usage below. *

* This call logically seals the transaction, which prevents the client from * further changing data tree using this transaction. Any subsequent calls to @@ -158,31 +138,65 @@ public interface AsyncWriteTransaction

, D> extends AsyncTransa * {@link IllegalStateException}. * * The transaction is marked as {@link TransactionStatus#SUBMITED} and - * enqueued into the data store backed for processing. + * enqueued into the data store back-end for processing. * *

* Whether or not the commit is successful is determined by versioning - * of data tree and validation of registered commit participants - * {@link AsyncConfigurationCommitHandler} - * if transaction changes {@link LogicalDatastoreType#CONFIGURATION} data tree. - *

- * The effects of successful commit of data depends on - * other data change listeners {@link AsyncDataChangeListener} and - * {@link AsyncConfigurationCommitHandler}, which was registered to the - * same {@link AsyncDataBroker}, to which this transaction belongs. - * + * of the data tree and validation of registered commit participants + * ({@link AsyncConfigurationCommitHandler}) + * if the transaction changes the data tree. + *

+ * The effects of a successful commit of data depends on data change listeners + * ({@link AsyncDataChangeListener}) and commit participants + * ({@link AsyncConfigurationCommitHandler}) that are registered with the data broker. + *

+ *

Example usage:

+ *
+     *  private void doWrite( final int tries ) {
+     *      WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+     *
+     *      MyDataObject data = ...;
+     *      InstanceIdentifier path = ...;
+     *      writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
+     *
+     *      Futures.addCallback( writeTx.submit(), new FutureCallback() {
+     *          public void onSuccess( Void result ) {
+     *              // succeeded
+     *          }
+     *
+     *          public void onFailure( Throwable t ) {
+     *              if( t instanceof OptimisticLockFailedException ) {
+     *                  if( ( tries - 1 ) > 0 ) {
+     *                      // do retry
+     *                      doWrite( tries - 1 );
+     *                  } else {
+     *                      // out of retries
+     *                  }
+     *              } else {
+     *                  // failed due to another type of TransactionCommitFailedException.
+     *              }
+     *          } );
+     * }
+     * ...
+     * doWrite( 2 );
+     * 
*

Failure scenarios

*

* Transaction may fail because of multiple reasons, such as *

    - *
  • Another transaction finished earlier and modified the same node in - * non-compatible way (see below). In this case the returned future will fail with + *
  • Another transaction finished earlier and modified the same node in a + * non-compatible way (see below). In this case the returned future will fail with an * {@link OptimisticLockFailedException}. It is the responsibility of the * caller to create a new transaction and submit the same modification again in - * order to update data tree.
  • + * order to update data tree. Warning: In most cases, retrying after an + * OptimisticLockFailedException will result in a high probability of success. + * However, there are scenarios, albeit unusual, where any number of retries will + * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3) + * to avoid an endless loop. + * *
  • Data change introduced by this transaction did not pass validation by * commit handlers or data was incorrectly structured. Returned future will - * fail with {@link DataValidationFailedException}. User should not retry to + * fail with a {@link DataValidationFailedException}. User should not retry to * create new transaction with same data, since it probably will fail again. *
  • *
@@ -273,8 +287,8 @@ public interface AsyncWriteTransaction

, D> extends AsyncTransa * txA.put(CONFIGURATION, PATH, A); // writes to PATH value A * txB.put(CONFIGURATION, PATH, B) // writes to PATH value B * - * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed - * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed + * ListenableFuture futureA = txA.submit(); // transaction A is sealed and submitted + * ListenebleFuture futureB = txB.submit(); // transaction B is sealed and submitted * * * Commit of transaction A will be processed asynchronously and data tree @@ -288,19 +302,21 @@ public interface AsyncWriteTransaction

, D> extends AsyncTransa * with {@link OptimisticLockFailedException} exception, which indicates to * client that concurrent transaction prevented the submitted transaction from being * applied. - * - * @return Result of the Commit, containing success information or list of - * encountered errors, if commit was not successful. The Future - * blocks until {@link TransactionStatus#COMMITED} is reached. - * Future will fail with {@link TransactionCommitFailedException} if - * Commit of this transaction failed. TODO: Usability: Consider - * change from ListenableFuture to - * {@link com.google.common.util.concurrent.CheckedFuture} which - * will throw {@link TransactionCommitFailedException}. + *
+ * @return a CheckFuture containing the result of the commit. The Future blocks until the + * commit operation is complete. A successful commit returns nothing. On failure, + * the Future will fail with a {@link TransactionCommitFailedException} or an exception + * derived from TransactionCommitFailedException. * * @throws IllegalStateException * if the transaction is not {@link TransactionStatus#NEW} */ - public ListenableFuture> commit(); + CheckedFuture submit(); + + /** + * @deprecated Use {@link #submit()} instead. + */ + @Deprecated + ListenableFuture> commit(); } diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java index c59c12ec5c..d48bfc79fe 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataValidationFailedException.java @@ -8,6 +8,8 @@ package org.opendaylight.controller.md.sal.common.api.data; import org.opendaylight.yangtools.concepts.Path; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import com.google.common.base.Preconditions; @@ -31,14 +33,17 @@ public class DataValidationFailedException extends TransactionCommitFailedExcept private Class> pathType; - public

> DataValidationFailedException(final Class

pathType,final P path, final String message, final Throwable cause) { - super(message, cause); + public

> DataValidationFailedException(final Class

pathType,final P path, + final String message, final Throwable cause) { + super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "invalid-value", message, null, + path != null ? path.toString() : null, cause)); this.pathType = Preconditions.checkNotNull(pathType, "path type must not be null"); this.path = Preconditions.checkNotNull(path,"path must not be null."); } - public

> DataValidationFailedException(final Class

pathType,final P path,final String message) { - this(pathType,path,message,null); + public

> DataValidationFailedException(final Class

pathType,final P path, + final String message) { + this(pathType, path, message, null); } public final Path getPath() { diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java index 222289ab6c..5ddec6b1ed 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/OptimisticLockFailedException.java @@ -1,5 +1,8 @@ package org.opendaylight.controller.md.sal.common.api.data; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; + /** * * Failure of asynchronous transaction commit caused by failure @@ -18,17 +21,13 @@ public class OptimisticLockFailedException extends TransactionCommitFailedExcept private static final long serialVersionUID = 1L; - protected OptimisticLockFailedException(final String message, final Throwable cause, final boolean enableSuppression, - final boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - public OptimisticLockFailedException(final String message, final Throwable cause) { - super(message, cause); + super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "resource-denied", + message, null, null, cause)); } public OptimisticLockFailedException(final String message) { - super(message); + this(message, null); } } diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java index f3c2e1093c..18a857e1d5 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitFailedException.java @@ -7,6 +7,15 @@ */ package org.opendaylight.controller.md.sal.common.api.data; +import java.util.Arrays; +import java.util.List; + +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; + +import com.google.common.collect.ImmutableList; + /** * * Failed commit of asynchronous transaction @@ -17,18 +26,39 @@ package org.opendaylight.controller.md.sal.common.api.data; */ public class TransactionCommitFailedException extends Exception { - private static final long serialVersionUID = -6138306275373237068L; + private static final long serialVersionUID = 1L; - protected TransactionCommitFailedException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); + private final List errorList; + + public TransactionCommitFailedException(final String message, final RpcError... errors) { + this(message, null, errors); } - public TransactionCommitFailedException(final String message, final Throwable cause) { + public TransactionCommitFailedException(final String message, final Throwable cause, + final RpcError... errors) { super(message, cause); + + if( errors != null && errors.length > 0 ) { + errorList = ImmutableList.builder().addAll( Arrays.asList( errors ) ).build(); + } + else { + // Add a default RpcError. + errorList = ImmutableList.of(RpcResultBuilder.newError(ErrorType.APPLICATION, null, + getMessage(), null, null, getCause())); + } } - public TransactionCommitFailedException(final String message) { - super(message); + /** + * Returns additional error information about this exception. + * + * @return a List of RpcErrors. There is always at least one RpcError. + */ + public List getErrorList() { + return errorList; } + @Override + public String getMessage() { + return new StringBuilder( super.getMessage() ).append(", errors: ").append( errorList ).toString(); + } } diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java index b030e6cb5f..d544c4b371 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/AbstractDataTransaction.java @@ -11,13 +11,19 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification; import org.opendaylight.yangtools.concepts.Path; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; public abstract class AbstractDataTransaction

, D extends Object> extends AbstractDataModification { @@ -83,18 +89,23 @@ public abstract class AbstractDataTransaction

, D extends Objec @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } AbstractDataTransaction other = (AbstractDataTransaction) obj; if (identifier == null) { - if (other.identifier != null) + if (other.identifier != null) { return false; - } else if (!identifier.equals(other.identifier)) + } + } else if (!identifier.equals(other.identifier)) { return false; + } return true; } @@ -122,4 +133,15 @@ public abstract class AbstractDataTransaction

, D extends Objec this.status = status; this.onStatusChange(status); } + + public static ListenableFuture> convertToLegacyCommitFuture( + CheckedFuture from ) { + return Futures.transform(from, new AsyncFunction>() { + @Override + public ListenableFuture> apply(Void input) throws Exception { + return Futures.immediateFuture(RpcResultBuilder. + success(TransactionStatus.COMMITED).build()); + } + } ); + } } diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/TwoPhaseCommit.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/TwoPhaseCommit.java index a51dc64816..007122e41d 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/TwoPhaseCommit.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/service/TwoPhaseCommit.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.md.sal.common.impl.service; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -21,10 +20,9 @@ import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener; 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.sal.common.util.Rpcs; import org.opendaylight.yangtools.concepts.Path; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,8 +66,7 @@ public class TwoPhaseCommit

, D extends Object, DCL extends Dat log.trace("Transaction: {} Finished successfully (no effects).", transactionId); - return Rpcs. getRpcResult(true, TransactionStatus.COMMITED, - Collections. emptySet()); + return RpcResultBuilder. success( TransactionStatus.COMMITED ).build(); } final ImmutableList.Builder> listenersBuilder = ImmutableList.builder(); @@ -127,8 +124,7 @@ public class TwoPhaseCommit

, D extends Object, DCL extends Dat log.trace("Transaction: {} Notifying listeners.", transactionId); publishDataChangeEvent(listeners); - return Rpcs. getRpcResult(true, TransactionStatus.COMMITED, - Collections. emptySet()); + return RpcResultBuilder. success(TransactionStatus.COMMITED).build(); } private void captureInitialState(ImmutableList> listeners) { @@ -240,7 +236,6 @@ public class TwoPhaseCommit

, D extends Object, DCL extends Dat for (final DataCommitTransaction transaction : transactions) { transaction.rollback(); } - Set _emptySet = Collections. emptySet(); - return Rpcs. getRpcResult(false, TransactionStatus.FAILED, _emptySet); + return RpcResultBuilder. failed().withResult(TransactionStatus.FAILED).build(); } } diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java index 1af7ccc79a..9aacf1e0a8 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java @@ -7,13 +7,11 @@ */ package org.opendaylight.controller.sal.common.util; -import java.util.Collections; - import org.opendaylight.controller.md.sal.common.api.data.DataModification; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; import org.opendaylight.yangtools.concepts.Path; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; public class CommitHandlerTransactions { @@ -26,11 +24,11 @@ public class CommitHandlerTransactions { } @Override public RpcResult rollback() throws IllegalStateException { - return Rpcs.getRpcResult(true, null, Collections.emptyList()); + return RpcResultBuilder.success().build(); } @Override public RpcResult finish() throws IllegalStateException { - return Rpcs.getRpcResult(true, null, Collections.emptyList()); + return RpcResultBuilder.success().build(); } @Override diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java index 4d41249b4d..cacb167f6f 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/RpcErrors.java @@ -12,9 +12,9 @@ import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; /** - * @author mirehak - * + * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder} */ +@Deprecated public class RpcErrors { /** 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 f303941871..69458b6a6d 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 @@ -16,6 +16,10 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import com.google.common.collect.ImmutableList; +/** + * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder} + */ +@Deprecated public class Rpcs { public static RpcResult getRpcResult(boolean successful) { diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java index 5baa5e72d3..0d5e47faee 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java @@ -8,9 +8,39 @@ package org.opendaylight.controller.md.sal.dom.api; import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; + +/** + * A transaction that provides read access to a logical data store. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}. + */ public interface DOMDataReadTransaction extends AsyncReadTransaction> { + /** + * Reads data from provided logical data store located at the provided path. + *

+ * If the target is a subtree, then the whole subtree is read (and will be + * accessible from the returned data object). + * + * @param store + * Logical data store from which read should occur. + * @param path + * Path which uniquely identifies subtree which client want to + * read + * @return Listenable Future which contains read result + *

    + *
  • If data at supplied path exists the + * {@link ListeblaFuture#get()} returns Optional object containing + * data once read is done. + *
  • If data at supplied path does not exists the + * {@link ListenbleFuture#get()} returns {@link Optional#absent()}. + *
+ */ + ListenableFuture>> read(LogicalDatastoreType store,InstanceIdentifier path); } diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java index 9415973de5..6a8977154c 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java @@ -8,9 +8,54 @@ package org.opendaylight.controller.md.sal.dom.api; import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +/** + * A transaction that provides mutation capabilities on a data tree. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + */ public interface DOMDataWriteTransaction extends AsyncWriteTransaction> { + /** + * Stores a piece of data at the specified path. This acts as an add / replace + * operation, which is to say that whole subtree will be replaced by the specified data. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + *

+ * If you need to make sure that a parent object exists but you do not want modify + * its pre-existing state by using put, consider using {@link #merge} instead. + * + * @param store + * the logical data store which should be modified + * @param path + * the data object path + * @param data + * the data object to be written to the specified path + * @throws IllegalStateException + * if the transaction has already been submitted + */ + void put(LogicalDatastoreType store, InstanceIdentifier path, NormalizedNode data); + + /** + * Merges a piece of data with the existing data at a specified path. Any pre-existing data + * which is not explicitly overwritten will be preserved. This means that if you store a container, + * its child lists will be merged. + *

+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}. + *

+ * If you require an explicit replace operation, use {@link #put} instead. + * + * @param store + * the logical data store which should be modified + * @param path + * the data object path + * @param data + * the data object to be merged to the specified path + * @throws IllegalStateException + * if the transaction has already been submitted + */ + void merge(LogicalDatastoreType store, InstanceIdentifier path, NormalizedNode data); } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java index 5694d0bca9..d354cca005 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMForwardedTransactionFactory.java @@ -114,7 +114,7 @@ public abstract class AbstractDOMForwardedTransactionFactory {@link DOMDataWriteTransaction#commit()} - results in invoking * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts * and then invoking finalized implementation callback - * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which + * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which * was commited and gathered results. * * @@ -167,7 +167,7 @@ public abstract class AbstractDOMForwardedTransactionFactory {@link DOMDataWriteTransaction#commit()} - results in invoking * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts * and then invoking finalized implementation callback - * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which + * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which * was commited and gathered results. *

  • * diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java index 7e37a1e3a3..7731646a57 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerImpl.java @@ -12,9 +12,9 @@ import static com.google.common.base.Preconditions.checkState; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicLong; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; @@ -23,14 +23,13 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.ListeningExecutorService; public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory implements DOMDataBroker, @@ -78,7 +77,7 @@ public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory> commit(final DOMDataWriteTransaction transaction, + public CheckedFuture submit(final DOMDataWriteTransaction transaction, final Iterable cohorts) { LOG.debug("Transaction: {} submitted with cohorts {}.", transaction.getIdentifier(), cohorts); return coordinator.submit(transaction, cohorts, Optional. absent()); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java index b9f096aafc..227693ca4d 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataBrokerTransactionChainImpl.java @@ -10,21 +10,20 @@ import java.util.concurrent.atomic.AtomicLong; import javax.annotation.concurrent.GuardedBy; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.CheckedFuture; /** * NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed @@ -73,7 +72,7 @@ public class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTrans } @Override - public synchronized ListenableFuture> commit( + public synchronized CheckedFuture submit( final DOMDataWriteTransaction transaction, final Iterable cohorts) { return coordinator.submit(transaction, cohorts, Optional. of(this)); } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java index 540e2fe20c..8b9eb445fd 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java @@ -6,20 +6,15 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import javax.annotation.concurrent.GuardedBy; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,18 +79,19 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { } @Override - public ListenableFuture> submit(final DOMDataWriteTransaction transaction, + public CheckedFuture submit(final DOMDataWriteTransaction transaction, final Iterable cohorts, final Optional listener) { Preconditions.checkArgument(transaction != null, "Transaction must not be null."); Preconditions.checkArgument(cohorts != null, "Cohorts must not be null."); Preconditions.checkArgument(listener != null, "Listener must not be null"); LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier()); - ListenableFuture> commitFuture = executor.submit(new CommitCoordinationTask( + ListenableFuture commitFuture = executor.submit(new CommitCoordinationTask( transaction, cohorts, listener)); if (listener.isPresent()) { Futures.addCallback(commitFuture, new DOMDataCommitErrorInvoker(transaction, listener.get())); } - return commitFuture; + + return Futures.makeChecked(commitFuture, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER); } /** @@ -141,7 +137,7 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { * support of cancelation. * */ - private static class CommitCoordinationTask implements Callable> { + private static class CommitCoordinationTask implements Callable { private final DOMDataWriteTransaction tx; private final Iterable cohorts; @@ -158,12 +154,13 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { } @Override - public RpcResult call() throws TransactionCommitFailedException { + public Void call() throws TransactionCommitFailedException { try { canCommitBlocking(); preCommitBlocking(); - return commitBlocking(); + commitBlocking(); + return null; } catch (TransactionCommitFailedException e) { LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, e); abortBlocking(e); @@ -219,9 +216,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor { * If one of cohorts failed preCommit * */ - private RpcResult commitBlocking() throws TransactionCommitFailedException { + private void commitBlocking() throws TransactionCommitFailedException { commitAll().checkedGet(); - return Rpcs.getRpcResult(true, TransactionStatus.COMMITED, Collections. emptySet()); } /** diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitErrorInvoker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitErrorInvoker.java index 811d4d8839..5ce9241dd2 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitErrorInvoker.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitErrorInvoker.java @@ -6,10 +6,7 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; -import org.opendaylight.yangtools.yang.common.RpcResult; - import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; @@ -22,7 +19,7 @@ import com.google.common.util.concurrent.FutureCallback; * callback is invoked with associated transaction and throwable is invoked on listener. * */ -class DOMDataCommitErrorInvoker implements FutureCallback> { +class DOMDataCommitErrorInvoker implements FutureCallback { private final DOMDataWriteTransaction tx; private final DOMDataCommitErrorListener listener; @@ -46,7 +43,7 @@ class DOMDataCommitErrorInvoker implements FutureCallback result) { + public void onSuccess(Void result) { // NOOP } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitExecutor.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitExecutor.java index 2050d148a8..234758ca75 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitExecutor.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitExecutor.java @@ -7,13 +7,11 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; -import org.opendaylight.yangtools.yang.common.RpcResult; - import com.google.common.base.Optional; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.CheckedFuture; /** * Executor of Three Phase Commit coordination for @@ -40,12 +38,12 @@ interface DOMDataCommitExecutor { * subtransactoins. * @param listener * Error listener which should be notified if transaction failed. - * @return ListenableFuture which contains RpcResult with - * {@link TransactionStatus#COMMITED} if commit coordination on - * cohorts finished successfully. + * @return a CheckedFuture. if commit coordination on cohorts finished successfully, + * nothing is returned from the Future, On failure, + * the Future fails with a {@link TransactionCommitFailedException}. * */ - ListenableFuture> submit(DOMDataWriteTransaction tx, + CheckedFuture submit(DOMDataWriteTransaction tx, Iterable cohort, Optional listener); } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitImplementation.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitImplementation.java index 4906b6e84d..2f2b6e508a 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitImplementation.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitImplementation.java @@ -7,12 +7,10 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; -import org.opendaylight.yangtools.yang.common.RpcResult; - -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.CheckedFuture; /** * @@ -23,10 +21,10 @@ import com.google.common.util.concurrent.ListenableFuture; public interface DOMDataCommitImplementation { /** - * User-supplied implementation of {@link DOMDataWriteTransaction#commit()} + * User-supplied implementation of {@link DOMDataWriteTransaction#submit()} * for transaction. * - * Callback invoked when {@link DOMDataWriteTransaction#commit()} is invoked + * Callback invoked when {@link DOMDataWriteTransaction#submit()} is invoked * on transaction created by this factory. * * @param transaction @@ -37,7 +35,7 @@ public interface DOMDataCommitImplementation { * commited transaction. * */ - ListenableFuture> commit(final DOMDataWriteTransaction transaction, + CheckedFuture submit(final DOMDataWriteTransaction transaction, final Iterable cohorts); } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java index f5b96e27f5..e1d27e453b 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java @@ -29,7 +29,7 @@ import com.google.common.util.concurrent.ListenableFuture; *
  • {@link #merge(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)} * * {@link #commit()} will result in invocation of - * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)} + * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)} * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying * transactions. * diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java index f791522a2a..5bddd763fb 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedWriteTransaction.java @@ -13,6 +13,8 @@ import javax.annotation.concurrent.GuardedBy; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; @@ -23,6 +25,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.ListenableFuture; /** @@ -39,7 +42,7 @@ import com.google.common.util.concurrent.ListenableFuture; * *

    * {@link #commit()} will result in invocation of - * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)} + * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)} * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying * transactions. * @@ -74,7 +77,7 @@ class DOMForwardedWriteTransaction extends * */ @GuardedBy("this") - private volatile ListenableFuture> commitFuture; + private volatile CheckedFuture commitFuture; protected DOMForwardedWriteTransaction(final Object identifier, final ImmutableMap backingTxs, final DOMDataCommitImplementation commitImpl) { @@ -119,6 +122,11 @@ class DOMForwardedWriteTransaction extends @Override public synchronized ListenableFuture> commit() { + return AbstractDataTransaction.convertToLegacyCommitFuture(submit()); + } + + @Override + public CheckedFuture submit() { checkNotReady(); ImmutableList.Builder cohortsBuilder = ImmutableList.builder(); @@ -126,7 +134,7 @@ class DOMForwardedWriteTransaction extends cohortsBuilder.add(subTx.ready()); } ImmutableList cohorts = cohortsBuilder.build(); - commitFuture = commitImpl.commit(this, cohorts); + commitFuture = commitImpl.submit(this, cohorts); /* *We remove reference to Commit Implementation in order @@ -148,5 +156,4 @@ class DOMForwardedWriteTransaction extends private void checkNotCommited() { checkState(commitFuture == null, "Transaction was already submited."); } - } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java index 27e322f23b..f57579858c 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleTransaction.java @@ -21,6 +21,7 @@ import java.util.concurrent.Future; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction; import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException; import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation; import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; @@ -204,7 +205,7 @@ DataModificationTransaction, Delegator { public Future> commit() { Preconditions.checkState(status == TransactionStatus.NEW); status = TransactionStatus.SUBMITED; - return getDelegate().commit(); + return AbstractDataTransaction.convertToLegacyCommitFuture(getDelegate().submit()); } @Override diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/NotificationModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/NotificationModule.java deleted file mode 100644 index b298a02a63..0000000000 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/NotificationModule.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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.sal.dom.broker; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.opendaylight.controller.sal.core.api.BrokerService; -import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality; -import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality; -import org.opendaylight.controller.sal.core.api.notify.NotificationListener; -import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService; -import org.opendaylight.controller.sal.core.api.notify.NotificationService; -import org.opendaylight.controller.sal.core.spi.BrokerModule; -import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; - -public class NotificationModule implements BrokerModule { - private static Logger log = LoggerFactory - .getLogger(NotificationModule.class); - - private final Multimap listeners = HashMultimap - .create(); - - private static final Set> PROVIDED_SERVICE_TYPE = ImmutableSet - .>of(NotificationService.class, - NotificationPublishService.class); - - private static final Set> SUPPORTED_CONSUMER_FUNCTIONALITY = ImmutableSet - .of((Class) NotificationListener.class, - NotificationListener.class); // Workaround: if we use the - // version of method with only - // one argument, the generics - // inference will not work - - @Override - public Set> getProvidedServices() { - return PROVIDED_SERVICE_TYPE; - } - - @Override - public Set> getSupportedConsumerFunctionality() { - return SUPPORTED_CONSUMER_FUNCTIONALITY; - } - - @Override - public T getServiceForSession(Class service, - ConsumerSession session) { - if (NotificationPublishService.class.equals(service) - && session instanceof ProviderSession) { - @SuppressWarnings("unchecked") - T ret = (T) newNotificationPublishService(session); - return ret; - } else if (NotificationService.class.equals(service)) { - - @SuppressWarnings("unchecked") - T ret = (T) newNotificationConsumerService(session); - return ret; - } - - throw new IllegalArgumentException( - "The requested session-specific service is not provided by this module."); - } - - private void sendNotification(CompositeNode notification) { - QName type = notification.getNodeType(); - Collection toNotify = listeners.get(type); - log.trace("Publishing notification " + type); - - if (toNotify == null) { - // No listeners were registered - returns. - return; - } - - for (NotificationListener listener : toNotify) { - try { - // FIXME: ensure that notification is immutable - listener.onNotification(notification); - } catch (Exception e) { - log.error("Uncaught exception in NotificationListener", e); - } - } - - } - - private NotificationService newNotificationConsumerService( - ConsumerSession session) { - return new NotificationConsumerSessionImpl(); - } - - private NotificationPublishService newNotificationPublishService( - ConsumerSession session) { - return new NotificationProviderSessionImpl(); - } - - private class NotificationConsumerSessionImpl implements - NotificationService { - - private final Multimap consumerListeners = HashMultimap - .create(); - private boolean closed = false; - - - @Override - public Registration addNotificationListener(QName notification, - NotificationListener listener) { - checkSessionState(); - if (notification == null) { - throw new IllegalArgumentException( - "Notification type must not be null."); - } - if (listener == null) { - throw new IllegalArgumentException("Listener must not be null."); - } - - consumerListeners.put(notification, listener); - listeners.put(notification, listener); - log.trace("Registered listener for notification: " + notification); - return null; // Return registration Object. - } - - public void removeNotificationListener(QName notification, - NotificationListener listener) { - checkSessionState(); - if (notification == null) { - throw new IllegalArgumentException( - "Notification type must not be null."); - } - if (listener == null) { - throw new IllegalArgumentException("Listener must not be null."); - } - consumerListeners.remove(notification, listener); - listeners.remove(notification, listener); - } - - public void closeSession() { - closed = true; - Map> toRemove = consumerListeners - .asMap(); - for (Entry> entry : toRemove - .entrySet()) { - listeners.remove(entry.getKey(), entry.getValue()); - } - } - - protected void checkSessionState() { - if (closed) - throw new IllegalStateException("Session is closed"); - } - } - - private class NotificationProviderSessionImpl extends - NotificationConsumerSessionImpl implements - NotificationPublishService { - - @Override - public void publish(CompositeNode notification) { - checkSessionState(); - if (notification == null) - throw new IllegalArgumentException( - "Notification must not be null."); - NotificationModule.this.sendNotification(notification); - } - } - - @Override - public Set> getSupportedProviderFunctionality() { - return Collections.emptySet(); - } -} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/GlobalRpcRegistration.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/GlobalRpcRegistration.java new file mode 100644 index 0000000000..f63e5ea768 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/GlobalRpcRegistration.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 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.sal.dom.broker.impl; + +import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.yang.common.QName; + +class GlobalRpcRegistration extends AbstractObjectRegistration implements + RpcRegistration { + private final QName type; + private SchemaAwareRpcBroker router; + + public GlobalRpcRegistration(final QName type, final RpcImplementation instance, final SchemaAwareRpcBroker router) { + super(instance); + this.type = type; + this.router = router; + } + + @Override + public QName getType() { + return type; + } + + @Override + protected void removeRegistration() { + if (router != null) { + router.remove(this); + router = null; + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java index 50dfbe852b..d7309b9ed3 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java @@ -7,7 +7,6 @@ */ package org.opendaylight.controller.sal.dom.broker.impl; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -15,10 +14,9 @@ import java.util.concurrent.ConcurrentHashMap; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; import org.opendaylight.controller.md.sal.common.api.data.DataModification; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.data.DataStore; -import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.slf4j.Logger; @@ -70,8 +68,7 @@ public final class HashMapDataStore implements DataStore, AutoCloseable { } public RpcResult rollback(HashMapDataStoreTransaction transaction) { - return Rpcs. getRpcResult(true, null, - Collections. emptySet()); + return RpcResultBuilder. success().build(); } public RpcResult finish(HashMapDataStoreTransaction transaction) { @@ -100,8 +97,7 @@ public final class HashMapDataStore implements DataStore, AutoCloseable { configuration.putAll(modification.getUpdatedConfigurationData()); operational.putAll(modification.getUpdatedOperationalData()); - return Rpcs. getRpcResult(true, null, - Collections. emptySet()); + return RpcResultBuilder. success().build(); } public void remove(final Map map, diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RoutedRpcRegImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RoutedRpcRegImpl.java new file mode 100644 index 0000000000..e4f19bbb2a --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RoutedRpcRegImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 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.sal.dom.broker.impl; + +import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; + +class RoutedRpcRegImpl extends AbstractObjectRegistration implements + RoutedRpcRegistration { + + private final QName type; + private final RoutedRpcSelector router; + + public RoutedRpcRegImpl(final QName rpcType, final RpcImplementation implementation, final RoutedRpcSelector routedRpcSelector) { + super(implementation); + this.type = rpcType; + router = routedRpcSelector; + } + + @Override + public void registerPath(final QName context, final InstanceIdentifier path) { + router.addPath(context, path, this); + } + + @Override + public void unregisterPath(final QName context, final InstanceIdentifier path) { + router.removePath(context, path, this); + } + + @Override + protected void removeRegistration() { + + } + + @Override + public QName getType() { + return type; + } + +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RoutedRpcSelector.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RoutedRpcSelector.java new file mode 100644 index 0000000000..a22aed7ab1 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/RoutedRpcSelector.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2014 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.sal.dom.broker.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy; +import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.controller.sal.core.api.RpcRoutingContext; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; + +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.ListenableFuture; + +class RoutedRpcSelector implements RpcImplementation, AutoCloseable, Identifiable { + + private final RpcRoutingStrategy strategy; + private final Set supportedRpcs; + private final RpcRoutingContext identifier; + final ConcurrentMap implementations = new ConcurrentHashMap<>(); + private final SchemaAwareRpcBroker router; + + public RoutedRpcSelector(final RpcRoutingStrategy strategy, final SchemaAwareRpcBroker router) { + super(); + this.strategy = strategy; + supportedRpcs = ImmutableSet.of(strategy.getIdentifier()); + identifier = RpcRoutingContext.create(strategy.getContext(), strategy.getIdentifier()); + this.router = router; + } + + @Override + public RpcRoutingContext getIdentifier() { + return identifier; + } + + @Override + public void close() throws Exception { + + } + + @Override + public Set getSupportedRpcs() { + return supportedRpcs; + } + + public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) { + return new RoutedRpcRegImpl(rpcType, implementation, this); + } + + @Override + public ListenableFuture> invokeRpc(final QName rpc, final CompositeNode input) { + CompositeNode inputContainer = input.getFirstCompositeByName(QName.create(rpc,"input")); + checkArgument(inputContainer != null, "Rpc payload must contain input element"); + SimpleNode routeContainer = inputContainer.getFirstSimpleByName(strategy.getLeaf()); + checkArgument(routeContainer != null, "Leaf %s must be set with value", strategy.getLeaf()); + Object route = routeContainer.getValue(); + checkArgument(route instanceof InstanceIdentifier, + "The routed node %s is not an instance identifier", route); + RpcImplementation potential = null; + if (route != null) { + RoutedRpcRegImpl potentialReg = implementations.get(route); + if (potentialReg != null) { + potential = potentialReg.getInstance(); + } + } + if (potential == null) { + return router.invokeRpc(rpc, (InstanceIdentifier) route, input); + } + checkState(potential != null, "No implementation is available for rpc:%s path:%s", rpc, route); + return potential.invokeRpc(rpc, input); + } + + public void addPath(final QName context, final InstanceIdentifier path, final RoutedRpcRegImpl routedRpcRegImpl) { + //checkArgument(strategy.getContext().equals(context),"Supplied context is not supported."); + RoutedRpcRegImpl previous = implementations.put(path, routedRpcRegImpl); + if (previous == null) { + router.notifyPathAnnouncement(context,strategy.getIdentifier(), path); + } + + } + + public void removePath(final QName context, final InstanceIdentifier path, final RoutedRpcRegImpl routedRpcRegImpl) { + boolean removed = implementations.remove(path, routedRpcRegImpl); + if (removed) { + router.notifyPathWithdrawal(context, strategy.getIdentifier(), path); + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareRpcBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareRpcBroker.java index 32139308b1..7bc827dcb0 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareRpcBroker.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/SchemaAwareRpcBroker.java @@ -14,10 +14,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import com.google.common.base.Preconditions; 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.impl.routing.RoutingUtils; +import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy; import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration; import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation; @@ -25,7 +25,6 @@ import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.controller.sal.core.api.RpcRegistrationListener; import org.opendaylight.controller.sal.core.api.RpcRoutingContext; import org.opendaylight.controller.sal.dom.broker.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; @@ -33,17 +32,13 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; +import com.google.common.base.Preconditions; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -53,8 +48,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareRpcBroker.class); - private static final QName CONTEXT_REFERENCE = QName.create("urn:opendaylight:yang:extension:yang-ext", - "2013-07-09", "context-reference"); + private final ListenerRegistry rpcRegistrationListeners = new ListenerRegistry<>(); private final ListenerRegistry> routeChangeListeners = new ListenerRegistry<>(); @@ -65,7 +59,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro private SchemaContextProvider schemaProvider; private RoutedRpcDefaultImplementation defaultDelegate; - public SchemaAwareRpcBroker(String identifier, SchemaContextProvider schemaProvider) { + public SchemaAwareRpcBroker(final String identifier, final SchemaContextProvider schemaProvider) { super(); this.identifier = identifier; this.schemaProvider = schemaProvider; @@ -75,7 +69,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro return defaultImplementation; } - public void setDefaultImplementation(RpcImplementation defaultImplementation) { + public void setDefaultImplementation(final RpcImplementation defaultImplementation) { this.defaultImplementation = defaultImplementation; } @@ -83,7 +77,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro return schemaProvider; } - public void setSchemaProvider(SchemaContextProvider schemaProvider) { + public void setSchemaProvider(final SchemaContextProvider schemaProvider) { this.schemaProvider = schemaProvider; } @@ -92,18 +86,18 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro } @Override - public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultDelegate) { + public void setRoutedRpcDefaultDelegate(final RoutedRpcDefaultImplementation defaultDelegate) { this.defaultDelegate = defaultDelegate; } @Override - public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) { + public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) { checkArgument(rpcType != null, "RPC Type should not be null"); checkArgument(implementation != null, "RPC Implementatoin should not be null"); return getOrCreateRoutedRpcRouter(rpcType).addRoutedRpcImplementation(rpcType, implementation); } - private RoutedRpcSelector getOrCreateRoutedRpcRouter(QName rpcType) { + private RoutedRpcSelector getOrCreateRoutedRpcRouter(final QName rpcType) { RoutedRpcSelector potential = getRoutedRpcRouter(rpcType); if (potential != null) { return potential; @@ -114,15 +108,15 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro return potential; } RpcDefinition definition = findRpcDefinition(rpcType); - RoutingStrategy strategy = getRoutingStrategy(definition); - checkState(strategy instanceof RoutedRpcStrategy, "Rpc %s is not routed.", rpcType); - potential = new RoutedRpcSelector((RoutedRpcStrategy) strategy, this); + RpcRoutingStrategy strategy = RpcRoutingStrategy.from(definition); + checkState(strategy.isContextBasedRouted(), "Rpc %s is not routed.", rpcType); + potential = new RoutedRpcSelector( strategy, this); implementations.put(rpcType, potential); return potential; } } - private RoutedRpcSelector getRoutedRpcRouter(QName rpcType) { + private RoutedRpcSelector getRoutedRpcRouter(final QName rpcType) { RpcImplementation potential = implementations.get(rpcType); if (potential != null) { checkState(potential instanceof RoutedRpcSelector, "Rpc %s is not routed.", rpcType); @@ -133,13 +127,13 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro } @Override - public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation) + public RpcRegistration addRpcImplementation(final QName rpcType, final RpcImplementation implementation) throws IllegalArgumentException { checkArgument(rpcType != null, "RPC Type should not be null"); checkArgument(implementation != null, "RPC Implementatoin should not be null"); checkState(!hasRpcImplementation(rpcType), "Implementation already registered"); RpcDefinition definition = findRpcDefinition(rpcType); - checkArgument(!isRoutedRpc(definition), "RPC Type must not be routed."); + checkArgument(!RpcRoutingStrategy.from(definition).isContextBasedRouted(), "RPC Type must not be content routed."); GlobalRpcRegistration reg = new GlobalRpcRegistration(rpcType, implementation, this); final RpcImplementation previous = implementations.putIfAbsent(rpcType, implementation); Preconditions.checkState(previous == null, "Rpc %s is already registered.",rpcType); @@ -147,7 +141,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro return reg; } - private void notifyRpcAdded(QName rpcType) { + private void notifyRpcAdded(final QName rpcType) { for (ListenerRegistration listener : rpcRegistrationListeners) { try { listener.getInstance().onRpcImplementationAdded(rpcType); @@ -158,12 +152,8 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro } } - private boolean isRoutedRpc(RpcDefinition definition) { - return getRoutingStrategy(definition) instanceof RoutedRpcStrategy; - } - @Override - public ListenerRegistration addRpcRegistrationListener(RpcRegistrationListener listener) { + public ListenerRegistration addRpcRegistrationListener(final RpcRegistrationListener listener) { ListenerRegistration reg = rpcRegistrationListeners.register(listener); for (QName impl : implementations.keySet()) { listener.onRpcImplementationAdded(impl); @@ -182,11 +172,11 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro } @Override - public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { + public ListenableFuture> invokeRpc(final QName rpc, final CompositeNode input) { return findRpcImplemention(rpc).invokeRpc(rpc, input); } - private RpcImplementation findRpcImplemention(QName rpc) { + private RpcImplementation findRpcImplemention(final QName rpc) { checkArgument(rpc != null, "Rpc name should not be null"); RpcImplementation potentialImpl = implementations.get(rpc); if (potentialImpl != null) { @@ -201,11 +191,11 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro return potentialImpl; } - private boolean hasRpcImplementation(QName rpc) { + private boolean hasRpcImplementation(final QName rpc) { return implementations.containsKey(rpc); } - private RpcDefinition findRpcDefinition(QName rpcType) { + private RpcDefinition findRpcDefinition(final QName rpcType) { checkArgument(rpcType != null, "Rpc name must be supplied."); checkState(schemaProvider != null, "Schema Provider is not available."); SchemaContext ctx = schemaProvider.getSchemaContext(); @@ -215,7 +205,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro return findRpcDefinition(rpcType, module.getRpcs()); } - static private RpcDefinition findRpcDefinition(QName rpcType, Set rpcs) { + static private RpcDefinition findRpcDefinition(final QName rpcType, final Set rpcs) { checkState(rpcs != null, "Rpc schema is not available."); for (RpcDefinition rpc : rpcs) { if (rpcType.equals(rpc.getQName())) { @@ -225,225 +215,17 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro throw new IllegalArgumentException("Supplied Rpc Type is not defined."); } - private RoutingStrategy getRoutingStrategy(RpcDefinition rpc) { - ContainerSchemaNode input = rpc.getInput(); - if (input != null) { - for (DataSchemaNode schemaNode : input.getChildNodes()) { - Optional context = getRoutingContext(schemaNode); - if (context.isPresent()) { - return createRoutedStrategy(rpc, context.get(), schemaNode.getQName()); - } - } - } - return createGlobalStrategy(rpc); - } - - private static RoutingStrategy createRoutedStrategy(RpcDefinition rpc, QName context, QName leafNode) { - return new RoutedRpcStrategy(rpc.getQName(), context, leafNode); - } - - private Optional getRoutingContext(DataSchemaNode schemaNode) { - for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) { - if (CONTEXT_REFERENCE.equals(extension.getNodeType())) { - return Optional.fromNullable(extension.getQName()); - } - } - return Optional.absent(); - } - - private static RoutingStrategy createGlobalStrategy(RpcDefinition rpc) { - GlobalRpcStrategy ret = new GlobalRpcStrategy(rpc.getQName()); - return ret; - } - @Override - public ListenableFuture> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) { - checkState(defaultDelegate != null); - return defaultDelegate.invokeRpc(rpc, identifier, input); - } - - private static abstract class RoutingStrategy implements Identifiable { - - private final QName identifier; - - public RoutingStrategy(QName identifier) { - super(); - this.identifier = identifier; - } - - @Override - public QName getIdentifier() { - return identifier; - } - } - - private static class GlobalRpcStrategy extends RoutingStrategy { - - public GlobalRpcStrategy(QName identifier) { - super(identifier); - } - } - - private static class RoutedRpcStrategy extends RoutingStrategy { - - private final QName context; - private final QName leaf; - - public RoutedRpcStrategy(QName identifier, QName ctx, QName leaf) { - super(identifier); - this.context = ctx; - this.leaf = leaf; - } - - public QName getContext() { - return context; - } - - public QName getLeaf() { - return leaf; - } + public ListenableFuture> invokeRpc(final QName rpc, final InstanceIdentifier route, final CompositeNode input) { + checkState(defaultDelegate != null, "No implementation is available for rpc:%s path:%s", rpc, route); + return defaultDelegate.invokeRpc(rpc, route, input); } - private static class RoutedRpcSelector implements RpcImplementation, AutoCloseable, Identifiable { - - private final RoutedRpcStrategy strategy; - private final Set supportedRpcs; - private final RpcRoutingContext identifier; - private RpcImplementation defaultDelegate; - private final ConcurrentMap implementations = new ConcurrentHashMap<>(); - private final SchemaAwareRpcBroker router; - - public RoutedRpcSelector(RoutedRpcStrategy strategy, SchemaAwareRpcBroker router) { - super(); - this.strategy = strategy; - supportedRpcs = ImmutableSet.of(strategy.getIdentifier()); - identifier = RpcRoutingContext.create(strategy.context, strategy.getIdentifier()); - this.router = router; - } - - @Override - public RpcRoutingContext getIdentifier() { - return identifier; - } - - @Override - public void close() throws Exception { - - } - - @Override - public Set getSupportedRpcs() { - return supportedRpcs; - } - - public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) { - return new RoutedRpcRegImpl(rpcType, implementation, this); - } - - @Override - public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { - CompositeNode inputContainer = input.getFirstCompositeByName(QName.create(rpc,"input")); - checkArgument(inputContainer != null, "Rpc payload must contain input element"); - SimpleNode routeContainer = inputContainer.getFirstSimpleByName(strategy.getLeaf()); - checkArgument(routeContainer != null, "Leaf %s must be set with value", strategy.getLeaf()); - Object route = routeContainer.getValue(); - checkArgument(route instanceof InstanceIdentifier, - "The routed node %s is not an instance identifier", route); - RpcImplementation potential = null; - if (route != null) { - RoutedRpcRegImpl potentialReg = implementations.get(route); - if (potentialReg != null) { - potential = potentialReg.getInstance(); - } - } - if (potential == null) { - return router.invokeRpc(rpc, (InstanceIdentifier) route, input); - } - checkState(potential != null, "No implementation is available for rpc:%s path:%s", rpc, route); - return potential.invokeRpc(rpc, input); - } - - public void addPath(QName context, InstanceIdentifier path, RoutedRpcRegImpl routedRpcRegImpl) { - //checkArgument(strategy.getContext().equals(context),"Supplied context is not supported."); - RoutedRpcRegImpl previous = implementations.put(path, routedRpcRegImpl); - if (previous == null) { - router.notifyPathAnnouncement(context,strategy.getIdentifier(), path); - } - - } - - public void removePath(QName context, InstanceIdentifier path, RoutedRpcRegImpl routedRpcRegImpl) { - boolean removed = implementations.remove(path, routedRpcRegImpl); - if (removed) { - router.notifyPathWithdrawal(context, strategy.getIdentifier(), path); - } - } - } - - private static class GlobalRpcRegistration extends AbstractObjectRegistration implements - RpcRegistration { - private final QName type; - private SchemaAwareRpcBroker router; - - public GlobalRpcRegistration(QName type, RpcImplementation instance, SchemaAwareRpcBroker router) { - super(instance); - this.type = type; - this.router = router; - } - - @Override - public QName getType() { - return type; - } - - @Override - protected void removeRegistration() { - if (router != null) { - router.remove(this); - router = null; - } - } - } - - private static class RoutedRpcRegImpl extends AbstractObjectRegistration implements - RoutedRpcRegistration { - - private final QName type; - private final RoutedRpcSelector router; - - public RoutedRpcRegImpl(QName rpcType, RpcImplementation implementation, RoutedRpcSelector routedRpcSelector) { - super(implementation); - this.type = rpcType; - router = routedRpcSelector; - } - - @Override - public void registerPath(QName context, InstanceIdentifier path) { - router.addPath(context, path, this); - } - - @Override - public void unregisterPath(QName context, InstanceIdentifier path) { - router.removePath(context, path, this); - } - - @Override - protected void removeRegistration() { - - } - - @Override - public QName getType() { - return type; - } - - } - - private void remove(GlobalRpcRegistration registration) { + void remove(final GlobalRpcRegistration registration) { implementations.remove(registration.getType(), registration); } - private void notifyPathAnnouncement(QName context, QName identifier, InstanceIdentifier path) { + void notifyPathAnnouncement(final QName context, final QName identifier, final InstanceIdentifier path) { RpcRoutingContext contextWrapped = RpcRoutingContext.create(context, identifier); RouteChange change = RoutingUtils.announcementChange(contextWrapped , path); for(ListenerRegistration> routeListener : routeChangeListeners) { @@ -451,15 +233,12 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro routeListener.getInstance().onRouteChange(change); } catch (Exception e) { LOG.error("Unhandled exception during invoking onRouteChange for {}",routeListener.getInstance(),e); - } } } - - - private void notifyPathWithdrawal(QName context,QName identifier, InstanceIdentifier path) { + void notifyPathWithdrawal(final QName context,final QName identifier, final InstanceIdentifier path) { RpcRoutingContext contextWrapped = RpcRoutingContext.create(context, identifier); RouteChange change = RoutingUtils.removalChange(contextWrapped , path); for(ListenerRegistration> routeListener : routeChangeListeners) { @@ -473,7 +252,7 @@ public class SchemaAwareRpcBroker implements RpcRouter, Identifiable, Ro @Override public > ListenerRegistration registerRouteChangeListener( - L listener) { + final L listener) { ListenerRegistration reg = routeChangeListeners.registerWithType(listener); RouteChange initial = createInitialRouteChange(); try { diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java index 29e078918e..2dec6f2e4d 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerPerformanceTest.java @@ -169,7 +169,7 @@ public class DOMBrokerPerformanceTest { public List> call() throws Exception { List> builder = new ArrayList<>(txNum); for (DOMDataReadWriteTransaction tx :transactions) { - builder.add(tx.commit()); + builder.add(tx.submit()); } return builder; } @@ -267,7 +267,7 @@ public class DOMBrokerPerformanceTest { measure("Txs:1 Submit", new Callable>() { @Override public ListenableFuture call() throws Exception { - return writeTx.commit(); + return writeTx.submit(); } }).get(); return null; diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java index fec73d665b..b006ca94e5 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java @@ -107,7 +107,7 @@ public class DOMBrokerTest { TestModel.TEST_PATH); assertTrue(writeTxContainer.get().isPresent()); - writeTx.commit().get(); + writeTx.submit().get(); Optional> afterCommitRead = domBroker.newReadOnlyTransaction() .read(OPERATIONAL, TestModel.TEST_PATH).get(); diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java index 38f08b30f9..3ea0bcefa5 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMTransactionChainTest.java @@ -7,7 +7,6 @@ */ package org.opendaylight.controller.md.sal.dom.broker.impl; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -21,7 +20,6 @@ import java.util.concurrent.TimeoutException; import org.junit.Before; import org.junit.Test; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; @@ -29,7 +27,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.md.sal.dom.store.impl.TestModel; import org.opendaylight.controller.sal.core.spi.data.DOMStore; -import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -80,7 +77,7 @@ public class DOMTransactionChainTest { * First transaction is marked as ready, we are able to allocate chained * transactions */ - ListenableFuture> firstWriteTxFuture = firstTx.commit(); + ListenableFuture firstWriteTxFuture = firstTx.submit(); /** * We alocate chained transaction - read transaction. @@ -126,7 +123,7 @@ public class DOMTransactionChainTest { /** * third transaction is sealed and commited */ - ListenableFuture> thirdDeleteTxFuture = thirdDeleteTx.commit(); + ListenableFuture thirdDeleteTxFuture = thirdDeleteTx.submit(); assertCommitSuccessful(thirdDeleteTxFuture); /** @@ -188,11 +185,9 @@ public class DOMTransactionChainTest { return tx; } - private static void assertCommitSuccessful(final ListenableFuture> future) + private static void assertCommitSuccessful(final ListenableFuture future) throws InterruptedException, ExecutionException { - RpcResult rpcResult = future.get(); - assertTrue(rpcResult.isSuccessful()); - assertEquals(TransactionStatus.COMMITED, rpcResult.getResult()); + future.get(); } private static void assertTestContainerExists(final DOMDataReadTransaction readTx) throws InterruptedException, diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java new file mode 100644 index 0000000000..81203c55fe --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014 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.md.sal.dom.broker.spi.rpc; + +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; + +import com.google.common.base.Optional; + +public abstract class RpcRoutingStrategy implements Identifiable { + + private final QName identifier; + private static final QName CONTEXT_REFERENCE = QName.create("urn:opendaylight:yang:extension:yang-ext", + "2013-07-09", "context-reference"); + + private RpcRoutingStrategy(final QName identifier) { + super(); + this.identifier = identifier; + } + + /** + * Returns leaf QName in which RPC Route is stored + * + * + * @return leaf QName in which RPC Route is stored + * @throws UnsupportedOperationException If RPC is not content routed. + * ({@link #isContextBasedRouted()} returned false) + */ + public abstract QName getLeaf(); + + /** + * Returns identity QName which represents RPC Routing context + * + * @return identity QName which represents RPC Routing context + * @throws UnsupportedOperationException If RPC is not content routed. + * ({@link #isContextBasedRouted()} returned false) + */ + public abstract QName getContext(); + + @Override + public QName getIdentifier() { + return identifier; + } + + /** + * Returns true if RPC is routed by context. + * + * @return true if RPC is routed by content. + */ + public abstract boolean isContextBasedRouted(); + + public static RpcRoutingStrategy from(final RpcDefinition rpc) { + ContainerSchemaNode input = rpc.getInput(); + if (input != null) { + for (DataSchemaNode schemaNode : input.getChildNodes()) { + Optional context = getRoutingContext(schemaNode); + if (context.isPresent()) { + return createRoutedStrategy(rpc, context.get(), schemaNode.getQName()); + } + } + } + return createGlobalStrategy(rpc); + } + + public static Optional getRoutingContext(final DataSchemaNode schemaNode) { + for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) { + if (CONTEXT_REFERENCE.equals(extension.getNodeType())) { + return Optional.fromNullable(extension.getQName()); + } + } + return Optional.absent(); + } + + private static RpcRoutingStrategy createRoutedStrategy(final RpcDefinition rpc, final QName context, final QName leafNode) { + return new RoutedRpcStrategy(rpc.getQName(), context, leafNode); + } + + + + private static RpcRoutingStrategy createGlobalStrategy(final RpcDefinition rpc) { + GlobalRpcStrategy ret = new GlobalRpcStrategy(rpc.getQName()); + return ret; + } + + private static class RoutedRpcStrategy extends RpcRoutingStrategy { + + final QName context; + private final QName leaf; + + private RoutedRpcStrategy(final QName identifier, final QName ctx, final QName leaf) { + super(identifier); + this.context = ctx; + this.leaf = leaf; + } + + @Override + public QName getContext() { + return context; + } + + @Override + public QName getLeaf() { + return leaf; + } + + @Override + public boolean isContextBasedRouted() { + return true; + } + } + + private static class GlobalRpcStrategy extends RpcRoutingStrategy { + + public GlobalRpcStrategy(final QName identifier) { + super(identifier); + } + + @Override + public boolean isContextBasedRouted() { + return false; + } + + @Override + public QName getContext() { + throw new UnsupportedOperationException("Not routed strategy does not have context."); + } + + @Override + public QName getLeaf() { + throw new UnsupportedOperationException("Not routed strategy does not have context."); + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java index 3ed02dfb41..01a5989dcd 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java @@ -1,14 +1,17 @@ package org.opendaylight.controller.config.yang.inmemory_datastore_provider; -import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.Executors; + import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import com.google.common.util.concurrent.MoreExecutors; + public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModule { - public InMemoryConfigDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } - public InMemoryConfigDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryConfigDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) { + public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryConfigDataStoreProviderModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @@ -19,7 +22,7 @@ public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.cont @Override public java.lang.AutoCloseable createInstance() { - InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.sameThreadExecutor()); + InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())); getSchemaServiceDependency().registerSchemaServiceListener(ids); return ids; } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java index eea95990a1..172b0dbc01 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java @@ -1,14 +1,17 @@ package org.opendaylight.controller.config.yang.inmemory_datastore_provider; -import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.Executors; + import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import com.google.common.util.concurrent.MoreExecutors; + public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModule { - public InMemoryOperationalDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } - public InMemoryOperationalDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryOperationalDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) { + public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryOperationalDataStoreProviderModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @@ -19,7 +22,7 @@ public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight @Override public java.lang.AutoCloseable createInstance() { - InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.sameThreadExecutor()); + InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())); getSchemaServiceDependency().registerSchemaServiceListener(ids); return ids; } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java new file mode 100644 index 0000000000..ee62c0bfbf --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaUpdateForTransactionTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014 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.md.sal.dom.store.impl; + +import static org.junit.Assert.assertNotNull; + +import java.util.concurrent.ExecutionException; + +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.yang.binding.YangModuleInfo; +import org.opendaylight.yangtools.yang.binding.util.BindingReflections; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +import com.google.common.base.Throwables; +import com.google.common.util.concurrent.MoreExecutors; + +public class SchemaUpdateForTransactionTest { + + private static final InstanceIdentifier TOP_PATH = InstanceIdentifier.of(Top.QNAME); + private SchemaContext schemaContext; + private InMemoryDOMDataStore domStore; + + @Before + public void setupStore() { + domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor()); + loadSchemas(RockTheHouseInput.class); + } + + public void loadSchemas(final Class... classes) { + YangModuleInfo moduleInfo; + try { + ModuleInfoBackedContext context = ModuleInfoBackedContext.create(); + for (Class clz : classes) { + moduleInfo = BindingReflections.getModuleInfo(clz); + + context.registerModuleInfo(moduleInfo); + } + schemaContext = context.tryToCreateSchemaContext().get(); + domStore.onGlobalContextUpdated(schemaContext); + } catch (Exception e) { + Throwables.propagateIfPossible(e); + } + } + + /** + * Test suite tests allocating transaction when schema context + * does not contain module necessary for client write, + * then triggering update of global schema context + * and then performing write (according to new module). + * + * If transaction between allocation and schema context was + * unmodified, it is safe to change its schema context + * to new one (e.g. it will be same as if allocated after + * schema context update.) + * + * @throws InterruptedException + * @throws ExecutionException + */ + @Test + public void testTransactionSchemaUpdate() throws InterruptedException, ExecutionException { + + assertNotNull(domStore); + + // We allocate transaction, initial schema context does not + // contain Lists model + DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction(); + assertNotNull(writeTx); + + // we trigger schema context update to contain Lists model + loadSchemas(RockTheHouseInput.class, Top.class); + + /** + * + * Writes /test in writeTx, this write should not fail + * with IllegalArgumentException since /test is in + * schema context. + * + */ + writeTx.write(TOP_PATH, ImmutableNodes.containerNode(Top.QNAME)); + + } + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java index dca8fcafef..de4ac7ac18 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -7,15 +7,19 @@ */ package org.opendaylight.controller.sal.connect.netconf; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import java.io.InputStream; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.ExecutorService; - import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDevice; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; @@ -36,9 +40,6 @@ import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; - /** * This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade */ @@ -53,6 +54,7 @@ public final class NetconfDevice implements RemoteDevice messageTransformer; private final SchemaContextProviderFactory schemaContextProviderFactory; private final SchemaSourceProviderFactory sourceProviderFactory; + private final NotificationHandler notificationHandler; public static NetconfDevice createNetconfDevice(final RemoteDeviceId id, final AbstractCachingSchemaSourceProvider schemaSourceProvider, @@ -79,6 +81,7 @@ public final class NetconfDevice implements RemoteDevice salFacade; + private final List cache = new LinkedList<>(); + private final MessageTransformer messageTransformer; + private boolean passNotifications = false; + private final RemoteDeviceId id; + + NotificationHandler(final RemoteDeviceHandler salFacade, final MessageTransformer messageTransformer, final RemoteDeviceId id) { + this.salFacade = salFacade; + this.messageTransformer = messageTransformer; + this.id = id; + } + + synchronized void handleNotification(final NetconfMessage notification) { + if(passNotifications) { + passNotification(messageTransformer.toNotification(notification)); + } else { + cacheNotification(notification); + } + } + + /** + * Forward all cached notifications and pass all notifications from this point directly to sal facade. + */ + synchronized void onRemoteSchemaUp() { + passNotifications = true; + + for (final NetconfMessage cachedNotification : cache) { + passNotification(messageTransformer.toNotification(cachedNotification)); + } + + cache.clear(); + } + + private void cacheNotification(final NetconfMessage notification) { + Preconditions.checkState(passNotifications == false); + + logger.debug("{}: Caching notification {}, remote schema not yet fully built", id, notification); + if(logger.isTraceEnabled()) { + logger.trace("{}: Caching notification {}", id, XmlUtil.toString(notification.getDocument())); + } + + cache.add(notification); + } + + private void passNotification(final CompositeNode parsedNotification) { + logger.debug("{}: Forwarding notification {}", id, parsedNotification); + Preconditions.checkNotNull(parsedNotification); + salFacade.onNotification(parsedNotification); + } + } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java index f36ad9abcb..8045f8cb4a 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java @@ -8,7 +8,6 @@ package org.opendaylight.controller.sal.connect.netconf.listener; import java.util.ArrayDeque; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Queue; @@ -26,16 +25,14 @@ import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguratio import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlUtil; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.connect.api.RemoteDevice; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; -import org.opendaylight.controller.sal.connect.util.FailedRpcResult; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; 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.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,9 +137,10 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, private RpcResult createErrorRpcResult( RpcError.ErrorType errorType, String message ) { - return new FailedRpcResult( RpcErrors.getRpcError( null, - NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(), - null, RpcError.ErrorSeverity.ERROR, message, errorType, null ) ); + return RpcResultBuilder.failed() + .withError( errorType, NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(), + message ) + .build(); } @Override @@ -208,8 +206,8 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, logger.warn( "{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", id, msgToS( request.request ), msgToS( message ), e ); - request.future.set( new FailedRpcResult( - NetconfMessageTransformUtil.toRpcError( e ) ) ); + request.future.set( RpcResultBuilder.failed() + .withRpcError( NetconfMessageTransformUtil.toRpcError( e ) ).build() ); return; } @@ -220,12 +218,12 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, logger.warn( "{}: Error reply from remote device, request: {}, response: {}", id, msgToS( request.request ), msgToS( message ), e ); - request.future.set( new FailedRpcResult( - NetconfMessageTransformUtil.toRpcError( e ) ) ); + request.future.set( RpcResultBuilder.failed() + .withRpcError( NetconfMessageTransformUtil.toRpcError( e ) ).build() ); return; } - request.future.set(Rpcs.getRpcResult( true, message, Collections.emptySet() ) ); + request.future.set( RpcResultBuilder.success( message ).build() ); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java index 8b6ac7d567..26c6a2758c 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceCommitHandler.java @@ -11,12 +11,11 @@ import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler; import org.opendaylight.controller.md.sal.common.api.data.DataModification; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.connect.util.FailedRpcResult; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.slf4j.Logger; @@ -74,8 +73,8 @@ public final class NetconfDeviceCommitHandler implements DataCommitHandler finish() throws IllegalStateException { - return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR, - id + ": Unexpected operation error during pre-commit operations", RpcError.ErrorType.APPLICATION, e)); + return RpcResultBuilder.failed().withError( RpcError.ErrorType.APPLICATION, + id + ": Unexpected operation error during pre-commit operations", e ).build(); } @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java index 6a62b1e20b..8d5b7aed2f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceRpc.java @@ -9,17 +9,19 @@ package org.opendaylight.controller.sal.connect.netconf.sal; import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; + import java.util.Collections; import java.util.Set; import javax.annotation.Nullable; + import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import com.google.common.util.concurrent.ListenableFuture; @@ -66,7 +68,8 @@ public final class NetconfDeviceRpc implements RpcImplementation { if (netconfMessageRpcResult.isSuccessful()) { return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc); } else { - return Rpcs.getRpcResult(false, netconfMessageRpcResult.getErrors()); + return RpcResultBuilder. failed() + .withRpcErrors(netconfMessageRpcResult.getErrors()).build(); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java index 1737b8234a..960f2ef2e8 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceTwoPhaseCommitTransaction.java @@ -33,18 +33,18 @@ import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; import org.opendaylight.controller.md.sal.common.api.data.DataModification; -import org.opendaylight.controller.sal.common.util.RpcErrors; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; -import org.opendaylight.controller.sal.connect.util.FailedRpcResult; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.RpcImplementation; 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; @@ -91,16 +91,14 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact } private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException { - sendEditRpc(createEditConfigStructure(key, Optional.absent(), Optional.of(value)), Optional.absent()); + sendEditRpc(createEditConfigStructure(key, Optional.absent(), Optional.of(value)), Optional.absent()); } private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException { - // FIXME use org.opendaylight.yangtools.yang.data.api.ModifyAction instead of strings - // TODO add string lowercase value to ModifyAction enum entries - sendEditRpc(createEditConfigStructure(toDelete, Optional.of("delete"), Optional.absent()), Optional.of("none")); + sendEditRpc(createEditConfigStructure(toDelete, Optional.of(ModifyAction.DELETE), Optional.absent()), Optional.of(ModifyAction.NONE)); } - private void sendEditRpc(final CompositeNode editStructure, final Optional defaultOperation) throws InterruptedException, ExecutionException { + private void sendEditRpc(final CompositeNode editStructure, final Optional defaultOperation) throws InterruptedException, ExecutionException { final ImmutableCompositeNode editConfigRequest = createEditConfigRequest(editStructure, defaultOperation); final RpcResult rpcResult = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, editConfigRequest).get(); @@ -111,7 +109,7 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact } } - private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure, final Optional defaultOperation) { + private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure, final Optional defaultOperation) { final CompositeNodeBuilder ret = ImmutableCompositeNode.builder(); // Target @@ -120,7 +118,7 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact // Default operation if(defaultOperation.isPresent()) { - SimpleNode defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, defaultOperation.get()); + final SimpleNode defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, modifyOperationToXmlString(defaultOperation.get())); ret.add(defOp); } @@ -135,7 +133,7 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact return ret.toInstance(); } - private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional operation, + private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional operation, final Optional lastChildOverride) { Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath); @@ -175,7 +173,7 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact return predicates; } - private CompositeNode getDeepestEditElement(final PathArgument arg, final Optional operation, final Optional lastChildOverride) { + private CompositeNode getDeepestEditElement(final PathArgument arg, final Optional operation, final Optional lastChildOverride) { final CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); builder.setQName(arg.getNodeType()); @@ -183,7 +181,7 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact addPredicatesToCompositeNodeBuilder(predicates, builder); if (operation.isPresent()) { - builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get()); + builder.setAttribute(NETCONF_OPERATION_QNAME, modifyOperationToXmlString(operation.get())); } if (lastChildOverride.isPresent()) { final List> children = lastChildOverride.get().getValue(); @@ -197,6 +195,10 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact return builder.toInstance(); } + private String modifyOperationToXmlString(final ModifyAction operation) { + return operation.name().toLowerCase(); + } + /** * Send commit rpc to finish the transaction * In case of failure or unexpected error response, ExecutionException is thrown @@ -211,8 +213,8 @@ final class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransact throw new RuntimeException(id + ": Interrupted while waiting for response", e); } catch (final ExecutionException e) { LOG.warn("{}: Failed to finish commit operation", id, e); - return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR, - id + ": Unexpected operation error during commit operation", RpcError.ErrorType.APPLICATION, e)); + return RpcResultBuilder.failed().withError( RpcError.ErrorType.APPLICATION, + id + ": Unexpected operation error during commit operation", e ).build(); } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java index f9e6239bed..80d0f67ac4 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java @@ -8,18 +8,19 @@ package org.opendaylight.controller.sal.connect.netconf.schema.mapping; import com.google.common.base.Optional; -import java.util.Collections; + import java.util.List; import java.util.Set; + import javax.activation.UnsupportedDataTypeException; + import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.controller.sal.connect.util.MessageCounter; 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; @@ -90,7 +91,7 @@ public class NetconfMessageTransformer implements MessageTransformeremptySet()); + return RpcResultBuilder.success( node ).build(); } } @@ -114,7 +115,7 @@ public class NetconfMessageTransformer implements MessageTransformer emptySet()); + return RpcResultBuilder.success( compositeNode ).build(); } @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java index 6d087a9cc3..0aeec64690 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java @@ -27,9 +27,9 @@ import javax.annotation.Nullable; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; -import org.opendaylight.controller.sal.common.util.RpcErrors; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; @@ -140,9 +140,14 @@ public class NetconfMessageTransformUtil { } } - return RpcErrors.getRpcError( null, ex.getErrorTag().getTagValue(), infoBuilder.toString(), - toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(), - toRpcErrorType( ex.getErrorType() ), ex.getCause() ); + ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() ); + return severity == ErrorSeverity.ERROR ? + RpcResultBuilder.newError( + toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(), + ex.getLocalizedMessage(), null, infoBuilder.toString(), ex.getCause() ) : + RpcResultBuilder.newWarning( + toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(), + ex.getLocalizedMessage(), null, infoBuilder.toString(), ex.getCause() ); } private static ErrorSeverity toRpcErrorSeverity( final NetconfDocumentedException.ErrorSeverity severity ) { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java deleted file mode 100644 index 49b16d4cfb..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/util/FailedRpcResult.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2014 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.sal.connect.util; - -import java.util.Collection; -import java.util.Collections; - -import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcResult; - -public final class FailedRpcResult implements RpcResult { - - private final RpcError rpcError; - - public FailedRpcResult(final RpcError rpcError) { - this.rpcError = rpcError; - } - - @Override - public boolean isSuccessful() { - return false; - } - - @Override - public T getResult() { - return null; - } - - @Override - public Collection getErrors() { - return Collections.singletonList(rpcError); - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java index c1b9f7b47b..defaab629f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java @@ -12,6 +12,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.InputStream; @@ -27,7 +28,6 @@ import org.junit.Test; import org.mockito.Mockito; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; @@ -38,8 +38,8 @@ import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransf import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.RpcImplementation; 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -65,8 +65,8 @@ public class NetconfDeviceTest { } } - private static final RpcResult rpcResult = Rpcs.getRpcResult(true, netconfMessage, Collections.emptySet()); - private static final RpcResult rpcResultC = Rpcs.getRpcResult(true, compositeNode, Collections.emptySet()); + private static final RpcResult rpcResult = RpcResultBuilder.success(netconfMessage).build(); + private static final RpcResult rpcResultC = RpcResultBuilder.success(compositeNode).build(); public static final String TEST_NAMESPACE = "test:namespace"; public static final String TEST_MODULE = "test-module"; @@ -83,6 +83,32 @@ public class NetconfDeviceTest { Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected(); } + @Test + public void testNotificationBeforeSchema() throws Exception { + final RemoteDeviceHandler facade = getFacade(); + final RemoteDeviceCommunicator listener = getListener(); + + final MessageTransformer messageTransformer = getMessageTransformer(); + final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, getSchemaContextProviderFactory(), getSourceProviderFactory()); + + device.onNotification(netconfMessage); + device.onNotification(netconfMessage); + + verify(facade, times(0)).onNotification(any(CompositeNode.class)); + + final NetconfSessionCapabilities sessionCaps = getSessionCaps(true, + Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION)); + + device.onRemoteSessionUp(sessionCaps, listener); + + verify(messageTransformer, timeout(10000).times(2)).toNotification(netconfMessage); + verify(facade, times(2)).onNotification(compositeNode); + + device.onNotification(netconfMessage); + verify(messageTransformer, times(3)).toNotification(netconfMessage); + verify(facade, times(3)).onNotification(compositeNode); + } + @Test public void testNetconfDeviceReconnect() throws Exception { final RemoteDeviceHandler facade = getFacade(); @@ -137,6 +163,7 @@ public class NetconfDeviceTest { final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class); doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); doNothing().when(remoteDeviceHandler).onDeviceDisconnected(); + doNothing().when(remoteDeviceHandler).onNotification(any(CompositeNode.class)); return remoteDeviceHandler; } @@ -174,6 +201,7 @@ public class NetconfDeviceTest { final MessageTransformer messageTransformer = mockClass(MessageTransformer.class); doReturn(netconfMessage).when(messageTransformer).toRpcRequest(any(QName.class), any(CompositeNode.class)); doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(QName.class)); + doReturn(compositeNode).when(messageTransformer).toNotification(any(NetconfMessage.class)); doNothing().when(messageTransformer).onGlobalContextUpdated(any(SchemaContext.class)); return messageTransformer; } @@ -198,4 +226,4 @@ public class NetconfDeviceTest { doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class)); return remoteDeviceCommunicator; } -} \ No newline at end of file +} diff --git a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java index 02deb5a815..28e358a06a 100644 --- a/opendaylight/md-sal/sal-protocolbuffer-encoding/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java +++ b/opendaylight/md-sal/sal-protocolbuffer-encoding/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java @@ -7,10 +7,31 @@ */ package org.opendaylight.controller.cluster.datastore.util; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URI; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + import org.custommonkey.xmlunit.Diff; import org.custommonkey.xmlunit.XMLUnit; import org.junit.Test; @@ -44,29 +65,10 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.TransformerFactoryConfigurationError; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.net.URI; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; /** @@ -96,8 +98,8 @@ public class NormalizedNodeXmlConverterTest { } } - public static DataSchemaNode getSchemaNode(SchemaContext context, - String moduleName, String childNodeName) { + public static DataSchemaNode getSchemaNode(final SchemaContext context, + final String moduleName, final String childNodeName) { for (Module module : context.getModules()) { if (module.getName().equals(moduleName)) { DataSchemaNode found = @@ -111,12 +113,13 @@ public class NormalizedNodeXmlConverterTest { + childNodeName); } - static DataSchemaNode findChildNode(Set children, String name) { + static DataSchemaNode findChildNode(final Set children, final String name) { List containers = Lists.newArrayList(); for (DataSchemaNode dataSchemaNode : children) { - if (dataSchemaNode.getQName().getLocalName().equals(name)) + if (dataSchemaNode.getQName().getLocalName().equals(name)) { return dataSchemaNode; + } if (dataSchemaNode instanceof DataNodeContainer) { containers.add((DataNodeContainer) dataSchemaNode); } else if (dataSchemaNode instanceof ChoiceNode) { @@ -135,13 +138,13 @@ public class NormalizedNodeXmlConverterTest { } private static InstanceIdentifier.NodeIdentifier getNodeIdentifier( - String localName) { - return new InstanceIdentifier.NodeIdentifier(new QName( + final String localName) { + return new InstanceIdentifier.NodeIdentifier(QName.create( URI.create(NAMESPACE), revision, localName)); } public static InstanceIdentifier.AugmentationIdentifier getAugmentIdentifier( - String... childNames) { + final String... childNames) { Set qn = Sets.newHashSet(); for (String childName : childNames) { @@ -233,7 +236,7 @@ public class NormalizedNodeXmlConverterTest { - public void init(String yangPath, String xmlPath, ContainerNode expectedNode) + public void init(final String yangPath, final String xmlPath, final ContainerNode expectedNode) throws Exception { SchemaContext schema = parseTestSchema(yangPath); this.xmlPath = xmlPath; @@ -242,7 +245,7 @@ public class NormalizedNodeXmlConverterTest { this.expectedNode = expectedNode; } - SchemaContext parseTestSchema(String yangPath) throws Exception { + SchemaContext parseTestSchema(final String yangPath) throws Exception { YangParserImpl yangParserImpl = new YangParserImpl(); InputStream stream = @@ -268,8 +271,9 @@ public class NormalizedNodeXmlConverterTest { .parse(Collections.singletonList(doc.getDocumentElement()), containerNode); - if (expectedNode != null) - junit.framework.Assert.assertEquals(expectedNode, built); + if (expectedNode != null) { + junit.framework.Assert.assertEquals(expectedNode, built); + } logger.info("{}", built); @@ -287,10 +291,9 @@ public class NormalizedNodeXmlConverterTest { System.out.println(toString(doc.getDocumentElement())); System.out.println(toString(el)); - boolean diff = - new Diff( - XMLUnit.buildControlDocument(toString(doc.getDocumentElement())), - XMLUnit.buildTestDocument(toString(el))).similar(); + new Diff( + XMLUnit.buildControlDocument(toString(doc.getDocumentElement())), + XMLUnit.buildTestDocument(toString(el))).similar(); } private static ContainerNode listLeafListWithAttributes() { @@ -353,8 +356,9 @@ public class NormalizedNodeXmlConverterTest { .parse(Collections.singletonList(doc.getDocumentElement()), containerNode); - if (expectedNode != null) - junit.framework.Assert.assertEquals(expectedNode, built); + if (expectedNode != null) { + junit.framework.Assert.assertEquals(expectedNode, built); + } logger.info("{}", built); @@ -372,14 +376,13 @@ public class NormalizedNodeXmlConverterTest { System.out.println(toString(doc.getDocumentElement())); System.out.println(toString(el)); - boolean diff = - new Diff( - XMLUnit.buildControlDocument(toString(doc.getDocumentElement())), - XMLUnit.buildTestDocument(toString(el))).similar(); + new Diff( + XMLUnit.buildControlDocument(toString(doc.getDocumentElement())), + XMLUnit.buildTestDocument(toString(el))).similar(); } - private Document loadDocument(String xmlPath) throws Exception { + private Document loadDocument(final String xmlPath) throws Exception { InputStream resourceAsStream = NormalizedNodeXmlConverterTest.class.getResourceAsStream(xmlPath); @@ -399,7 +402,7 @@ public class NormalizedNodeXmlConverterTest { BUILDERFACTORY = factory; } - private Document readXmlToDocument(InputStream xmlContent) + private Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException { DocumentBuilder dBuilder; try { @@ -413,7 +416,7 @@ public class NormalizedNodeXmlConverterTest { return doc; } - public static String toString(Element xml) { + public static String toString(final Element xml) { try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); @@ -442,11 +445,10 @@ public class NormalizedNodeXmlConverterTest { System.out.println(toString(convertedDoc.getDocumentElement())); XMLUnit.setIgnoreWhitespace(true); XMLUnit.setIgnoreComments(true); - boolean diff = - new Diff(XMLUnit.buildControlDocument(toString(expectedDoc - .getDocumentElement())), - XMLUnit.buildTestDocument(toString(convertedDoc - .getDocumentElement()))).similar(); + new Diff(XMLUnit.buildControlDocument(toString(expectedDoc + .getDocumentElement())), + XMLUnit.buildTestDocument(toString(convertedDoc + .getDocumentElement()))).similar(); System.out.println(toString(expectedDoc.getDocumentElement())); } @@ -464,11 +466,10 @@ public class NormalizedNodeXmlConverterTest { System.out.println(toString(convertedDoc.getDocumentElement())); XMLUnit.setIgnoreWhitespace(true); XMLUnit.setIgnoreComments(true); - boolean diff = - new Diff(XMLUnit.buildControlDocument(toString(expectedDoc - .getDocumentElement())), - XMLUnit.buildTestDocument(toString(convertedDoc - .getDocumentElement()))).similar(); + new Diff(XMLUnit.buildControlDocument(toString(expectedDoc + .getDocumentElement())), + XMLUnit.buildTestDocument(toString(convertedDoc + .getDocumentElement()))).similar(); System.out.println(toString(expectedDoc.getDocumentElement())); // now we will try to convert xml back to normalize node. diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java index a2c228788f..ead1740ffd 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java @@ -8,12 +8,13 @@ package org.opendaylight.controller.sal.restconf.impl; import com.google.common.util.concurrent.Futures; -import java.util.Collections; + import java.util.concurrent.Future; + import javax.ws.rs.core.Response.Status; + import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.DataReader; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession; import org.opendaylight.controller.sal.core.api.data.DataBrokerService; import org.opendaylight.controller.sal.core.api.data.DataChangeListener; @@ -24,8 +25,8 @@ import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter; import org.opendaylight.yangtools.concepts.ListenerRegistration; 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.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.slf4j.Logger; @@ -176,8 +177,8 @@ public class BrokerFacade implements DataReader getRpcResult(true, TransactionStatus.COMMITED, - Collections. emptyList())); + return Futures.immediateFuture(RpcResultBuilder. + success(TransactionStatus.COMMITED).build()); } transaction.removeConfigurationData(path); return transaction.commit(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java index 92a7014438..611fb49090 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java @@ -84,7 +84,7 @@ public class RestCodec { if (input instanceof IdentityValuesDTO) { return identityrefCodec.deserialize(input); } - logger.info( + logger.debug( "Value is not instance of IdentityrefTypeDefinition but is {}. Therefore NULL is used as translation of - {}", input == null ? "null" : input.getClass(), String.valueOf(input)); return null; diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index bb7547d330..4e807b4e23 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -490,7 +490,13 @@ public class RestconfImpl implements RestconfService { } final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded); - RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded); + + RpcDefinition rpc = null; + if (mountPoint == null) { + rpc = controllerContext.getRpcDefinition(identifierDecoded); + } else { + rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded); + } if (rpc == null) { throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT); @@ -504,6 +510,25 @@ public class RestconfImpl implements RestconfService { } + private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) { + final String[] splittedIdentifier = identifierDecoded.split(":"); + if (splittedIdentifier.length != 2) { + throw new RestconfDocumentedException(identifierDecoded + + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE); + } + for (Module module : schemaContext.getModules()) { + if (module.getName().equals(splittedIdentifier[0])) { + for (RpcDefinition rpcDefinition : module.getRpcs()) { + if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) { + return rpcDefinition; + } + } + } + } + return null; + } + private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, boolean prettyPrint) { if (rpcExecutor == null) { throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java index d12bf9e44e..2adf9b5530 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -36,8 +36,6 @@ import javax.ws.rs.core.UriInfo; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; @@ -50,8 +48,8 @@ import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; import org.opendaylight.controller.sal.restconf.impl.StructuredData; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; @@ -112,7 +110,7 @@ public class InvokeRpcMethodTest { CompositeNode payload = preparePayload(); when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn( - Futures.> immediateFuture(Rpcs. getRpcResult(true))); + Futures.> immediateFuture(RpcResultBuilder.success().build())); StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo); assertTrue(structData == null); @@ -132,7 +130,7 @@ public class InvokeRpcMethodTest { @Test public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() { - RpcResult rpcResult = Rpcs. getRpcResult(false); + RpcResult rpcResult = RpcResultBuilder.failed().build(); BrokerFacade brokerFacade = mock(BrokerFacade.class); when( @@ -177,11 +175,13 @@ public class InvokeRpcMethodTest { @Test public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() { - List rpcErrors = Arrays.asList(RpcErrors.getRpcError(null, "bogusTag", null, ErrorSeverity.ERROR, - "foo", RpcError.ErrorType.TRANSPORT, null), RpcErrors.getRpcError("app-tag", "in-use", null, - ErrorSeverity.WARNING, "bar", RpcError.ErrorType.RPC, null)); + List rpcErrors = Arrays.asList( + RpcResultBuilder.newError( RpcError.ErrorType.TRANSPORT, "bogusTag", "foo" ), + RpcResultBuilder.newWarning( RpcError.ErrorType.RPC, "in-use", "bar", + "app-tag", null, null ) ); - RpcResult rpcResult = Rpcs. getRpcResult(false, rpcErrors); + RpcResult rpcResult = RpcResultBuilder.failed() + .withRpcErrors(rpcErrors).build(); BrokerFacade brokerFacade = mock(BrokerFacade.class); when( @@ -205,7 +205,7 @@ public class InvokeRpcMethodTest { @Test public void testInvokeRpcWithNoPayload_Success() { - RpcResult rpcResult = Rpcs. getRpcResult(true); + RpcResult rpcResult = RpcResultBuilder.success().build(); BrokerFacade brokerFacade = mock(BrokerFacade.class); when( @@ -246,7 +246,7 @@ public class InvokeRpcMethodTest { @Test public void testInvokeRpcMethodWithInput() { - RpcResult rpcResult = Rpcs. getRpcResult(true); + RpcResult rpcResult = RpcResultBuilder.success().build(); CompositeNode payload = mock(CompositeNode.class); @@ -279,8 +279,8 @@ public class InvokeRpcMethodTest { @Test public void testInvokeRpcWithNoPayloadWithOutput_Success() { CompositeNode compositeNode = mock(CompositeNode.class); - RpcResult rpcResult = Rpcs. getRpcResult(true, compositeNode, - Collections. emptyList()); + RpcResult rpcResult = + RpcResultBuilder.success(compositeNode).build(); BrokerFacade brokerFacade = mock(BrokerFacade.class); when( @@ -297,9 +297,17 @@ public class InvokeRpcMethodTest { assertNotNull(output.getSchema()); } + /** + * + * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context. + * This rpc is then executed. + * + * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is + * invoked. + */ @Test public void testMountedRpcCallNoPayload_Success() throws Exception { - RpcResult rpcResult = Rpcs. getRpcResult(true); + RpcResult rpcResult = RpcResultBuilder.success().build(); ListenableFuture> mockListener = mock(ListenableFuture.class); when(mockListener.get()).thenReturn(rpcResult); @@ -312,21 +320,32 @@ public class InvokeRpcMethodTest { MountInstance mockMountPoint = mock(MountInstance.class); when(mockMountPoint.rpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener); + when(mockMountPoint.getSchemaContext()).thenReturn(TestUtils.loadSchemaContext("/invoke-rpc")); + InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class); when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint); ControllerContext mockedContext = mock(ControllerContext.class); - String cancelToastStr = "toaster:cancel-toast"; - when(mockedContext.urlPathArgDecode(cancelToastStr)).thenReturn(cancelToastStr); - when(mockedContext.getRpcDefinition(cancelToastStr)).thenReturn(mockRpc); + String rpcNoop = "invoke-rpc-module:rpc-noop"; + when(mockedContext.urlPathArgDecode(rpcNoop)).thenReturn(rpcNoop); + when(mockedContext.getRpcDefinition(rpcNoop)).thenReturn(mockRpc); when( - mockedContext.toMountPointIdentifier("opendaylight-inventory:nodes/node/" - + "REMOTE_HOST/yang-ext:mount/toaster:cancel-toast")).thenReturn(mockedInstanceId); + mockedContext.toMountPointIdentifier(eq("opendaylight-inventory:nodes/node/" + + "REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop"))).thenReturn(mockedInstanceId); restconfImpl.setControllerContext(mockedContext); - StructuredData output = restconfImpl.invokeRpc( - "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/toaster:cancel-toast", "", uriInfo); - assertEquals(null, output); + try { + restconfImpl.invokeRpc( + "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop", "", + uriInfo); + fail("RestconfDocumentedException wasn't raised"); + } catch (RestconfDocumentedException e) { + List errors = e.getErrors(); + assertNotNull(errors); + assertEquals(1, errors.size()); + assertEquals(ErrorType.APPLICATION, errors.iterator().next().getErrorType()); + assertEquals(ErrorTag.OPERATION_FAILED, errors.iterator().next().getErrorTag()); + } // additional validation in the fact that the restconfImpl does not // throw an exception. diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java index 536d140cf1..ff4678d36f 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java @@ -10,6 +10,7 @@ package org.opendaylight.controller.sal.restconf.impl.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; @@ -17,8 +18,6 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -28,16 +27,20 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; + import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.BeforeClass; @@ -64,11 +67,16 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + public class RestGetOperationTest extends JerseyTest { static class NodeData { @@ -90,6 +98,8 @@ public class RestGetOperationTest extends JerseyTest { private static SchemaContext schemaContextModules; private static SchemaContext schemaContextBehindMountPoint; + private static final String RESTCONF_NS = "urn:ietf:params:xml:ns:yang:ietf-restconf"; + @BeforeClass public static void init() throws FileNotFoundException { schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs"); @@ -241,7 +251,7 @@ public class RestGetOperationTest extends JerseyTest { validateModulesResponseJson(response); response = target(uri).request("application/yang.api+xml").get(); - validateModulesResponseXml(response); + validateModulesResponseXml(response,schemaContextModules); } // /streams/ @@ -257,9 +267,12 @@ public class RestGetOperationTest extends JerseyTest { assertTrue(responseBody.contains("streams")); response = target(uri).request("application/yang.api+xml").get(); - responseBody = response.readEntity(String.class); - assertNotNull(responseBody); - assertTrue(responseBody.contains(" foundOperations = new HashSet<>(); + + NodeList operationsList = operationsElem.getChildNodes(); + for(int i = 0;i < operationsList.getLength();i++) { + org.w3c.dom.Node operation = operationsList.item(i); + + String namespace = operation.getNamespaceURI(); + String name = operation.getLocalName(); + QName opQName = QName.create(URI.create(namespace), null, name); + foundOperations.add(opQName); + } + + for(RpcDefinition schemaOp : schemaContext.getOperations()) { + assertTrue(foundOperations.contains(schemaOp.getQName().withoutRevision())); + } + + } + // /operations/pathToMountPoint/yang-ext:mount @Test public void getOperationsBehindMountPointTest() throws FileNotFoundException, UnsupportedEncodingException { @@ -337,15 +372,13 @@ public class RestGetOperationTest extends JerseyTest { Response response = target(uri).request("application/yang.api+xml").get(); assertEquals(200, response.getStatus()); - String responseBody = response.readEntity(String.class); - assertTrue("Xml response for /operations/mount_point rpc-behind-module1 is incorrect", - validateOperationsResponseXml(responseBody, "rpc-behind-module1", "module:1:behind:mount:point").find()); - assertTrue("Xml response for /operations/mount_point rpc-behind-module2 is incorrect", - validateOperationsResponseXml(responseBody, "rpc-behind-module2", "module:2:behind:mount:point").find()); + + Document responseDoc = response.readEntity(Document.class); + validateOperationsResponseXml(responseDoc, schemaContextBehindMountPoint); response = target(uri).request("application/yang.api+json").get(); assertEquals(200, response.getStatus()); - responseBody = response.readEntity(String.class); + String responseBody = response.readEntity(String.class); assertTrue("Json response for /operations/mount_point rpc-behind-module1 is incorrect", validateOperationsResponseJson(responseBody, "rpc-behind-module1", "module1-behind-mount-point").find()); assertTrue("Json response for /operations/mount_point rpc-behind-module2 is incorrect", @@ -441,15 +474,7 @@ public class RestGetOperationTest extends JerseyTest { response = target(uri).request("application/yang.api+xml").get(); assertEquals(200, response.getStatus()); - responseBody = response.readEntity(String.class); - assertTrue( - "module1-behind-mount-point in json wasn't found", - prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody) - .find()); - assertTrue( - "module2-behind-mount-point in json wasn't found", - prepareXmlRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point", responseBody) - .find()); + validateModulesResponseXml(response, schemaContextBehindMountPoint); } @@ -481,26 +506,83 @@ public class RestGetOperationTest extends JerseyTest { response = target(uri).request("application/yang.api+xml").get(); assertEquals(200, response.getStatus()); - responseBody = response.readEntity(String.class); - assertTrue( - "module1-behind-mount-point in json wasn't found", - prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody) - .find()); - split = responseBody.split(" 0); + + HashSet foundModules = new HashSet<>(); + + for(int i=0;i < moduleNodes.getLength();i++) { + org.w3c.dom.Node module = moduleNodes.item(i); + + QName name = assertedModuleXmlToModuleQName(module); + foundModules.add(name); + } + + assertAllModules(foundModules,schemaContext); + } + + private void assertAllModules(final Set foundModules, final SchemaContext schemaContext) { + for(Module module : schemaContext.getModules()) { + QName current = QName.create(module.getQNameModule(),module.getName()); + assertTrue("Module not found in response.",foundModules.contains(current)); + } + + } + + private QName assertedModuleXmlToModuleQName(final org.w3c.dom.Node module) { + assertEquals("module", module.getLocalName()); + assertEquals(RESTCONF_NS, module.getNamespaceURI()); + String revision = null; + String namespace = null; + String name = null; + + + NodeList childNodes = module.getChildNodes(); + + for(int i =0;i < childNodes.getLength(); i++) { + org.w3c.dom.Node child = childNodes.item(i); + assertEquals(RESTCONF_NS, child.getNamespaceURI()); + + switch(child.getLocalName()) { + case "name": + assertNull("Name element appeared multiple times",name); + name = child.getTextContent().trim(); + break; + case "revision": + assertNull("Revision element appeared multiple times",revision); + revision = child.getTextContent().trim(); + break; + + case "namespace": + assertNull("Namespace element appeared multiple times",namespace); + namespace = child.getTextContent().trim(); + break; + } + } + + assertNotNull("Revision was not part of xml",revision); + assertNotNull("Module namespace was not part of xml",namespace); + assertNotNull("Module identiffier was not part of xml",name); + + + // TODO Auto-generated method stub + + return QName.create(namespace,revision,name); } private void validateModulesResponseJson(final Response response) { @@ -542,34 +624,6 @@ public class RestGetOperationTest extends JerseyTest { } - private Matcher prepareXmlRegex(final String module, final String revision, final String namespace, - final String searchIn) { - StringBuilder regex = new StringBuilder(); - regex.append("^"); - - regex.append(".*"); - - regex.append(".*"); - regex.append(".*" + module); - regex.append(".*<\\/name>"); - - regex.append(".*"); - regex.append(".*" + revision); - regex.append(".*<\\/revision>"); - - regex.append(".*"); - regex.append(".*" + namespace); - regex.append(".*<\\/namespace>"); - - regex.append(".*<\\/module.*>"); - - regex.append(".*"); - regex.append("$"); - - Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL); - return ptrn.matcher(searchIn); - } private void prepareMockForModulesTest(final ControllerContext mockedControllerContext) throws FileNotFoundException { @@ -626,7 +680,7 @@ public class RestGetOperationTest extends JerseyTest { getDataWithUriIncludeWhiteCharsParameter("operational"); } - private void getDataWithUriIncludeWhiteCharsParameter(String target) throws UnsupportedEncodingException { + private void getDataWithUriIncludeWhiteCharsParameter(final String target) throws UnsupportedEncodingException { mockReadConfigurationDataMethod(); String uri = "/" + target + "/ietf-interfaces:interfaces/interface/eth0"; Response response = target(uri).queryParam("prettyPrint", "false").request("application/xml").get(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java index 1442338e2c..af9dd28359 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java @@ -37,7 +37,6 @@ import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; -import org.opendaylight.controller.sal.common.util.RpcErrors; import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.core.api.mount.MountService; import org.opendaylight.controller.sal.rest.api.Draft02; @@ -52,7 +51,7 @@ import org.opendaylight.controller.sal.restconf.impl.ControllerContext; import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -125,10 +124,10 @@ public class RestPostOperationTest extends JerseyTest { assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput)); List rpcErrors = new ArrayList<>(); - rpcErrors.add(RpcErrors.getRpcError("applicationTag1", "tag1", "info1", ErrorSeverity.ERROR, "message1", - ErrorType.RPC, null)); - rpcErrors.add(RpcErrors.getRpcError("applicationTag2", "tag2", "info2", ErrorSeverity.WARNING, "message2", - ErrorType.PROTOCOL, null)); + rpcErrors.add( RpcResultBuilder.newError( ErrorType.RPC, "tag1", "message1", + "applicationTag1", "info1", null ) ); + rpcErrors.add( RpcResultBuilder.newWarning( ErrorType.PROTOCOL, "tag2", "message2", + "applicationTag2", "info2", null ) ); mockInvokeRpc(null, false, rpcErrors); assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput)); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java index f0d3fb6822..18311104a4 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java @@ -11,7 +11,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; -import static org.opendaylight.controller.sal.common.util.RpcErrors.getRpcError; import java.util.HashMap; import java.util.Map; @@ -23,6 +22,7 @@ import org.opendaylight.controller.sal.restconf.impl.RestconfError; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; /** * Unit tests for RestconfError. @@ -144,41 +144,41 @@ public class RestconfErrorTest { public void testRestConfErrorWithRpcError() { // All fields set - RpcError rpcError = getRpcError("mock app-tag", ErrorTag.BAD_ATTRIBUTE.getTagValue(), "mock error-info", - RpcError.ErrorSeverity.ERROR, "mock error-message", RpcError.ErrorType.PROTOCOL, new Exception( - "mock cause")); + RpcError rpcError = RpcResultBuilder.newError( + RpcError.ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE.getTagValue(), "mock error-message", + "mock app-tag", "mock error-info", new Exception( "mock cause" ) ); validateRestConfError("mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock app-tag", "mock error-info", new RestconfError(rpcError)); // All fields set except 'info' - expect error-info set to 'cause' - rpcError = getRpcError("mock app-tag", ErrorTag.BAD_ATTRIBUTE.getTagValue(), null, - RpcError.ErrorSeverity.ERROR, "mock error-message", RpcError.ErrorType.PROTOCOL, new Exception( - "mock cause")); + rpcError = RpcResultBuilder.newError( + RpcError.ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE.getTagValue(), "mock error-message", + "mock app-tag", null, new Exception( "mock cause" ) ); validateRestConfError("mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock app-tag", new Contains("mock cause"), new RestconfError(rpcError)); // Some fields set - expect error-info set to ErrorSeverity - rpcError = getRpcError(null, ErrorTag.ACCESS_DENIED.getTagValue(), null, RpcError.ErrorSeverity.ERROR, null, - RpcError.ErrorType.RPC, null); + rpcError = RpcResultBuilder.newError( + RpcError.ErrorType.RPC, ErrorTag.ACCESS_DENIED.getTagValue(), null, null, null, null ); validateRestConfError(null, ErrorType.RPC, ErrorTag.ACCESS_DENIED, null, "error", new RestconfError(rpcError)); // 'tag' field not mapped to ErrorTag - expect error-tag set to // OPERATION_FAILED - rpcError = getRpcError(null, "not mapped", null, RpcError.ErrorSeverity.WARNING, null, - RpcError.ErrorType.TRANSPORT, null); + rpcError = RpcResultBuilder.newWarning( + RpcError.ErrorType.TRANSPORT, "not mapped", null, null, null, null ); validateRestConfError(null, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED, null, "warning", new RestconfError(rpcError)); // No fields set - edge case - rpcError = getRpcError(null, null, null, null, null, null, null); + rpcError = RpcResultBuilder.newError( null, null, null, null, null, null ); - validateRestConfError(null, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, null, (String) null, - new RestconfError(rpcError)); + validateRestConfError( null, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, + null, "error", new RestconfError( rpcError ) ); } private void validateRestConfError(String expectedMessage, ErrorType expectedErrorType, ErrorTag expectedErrorTag, diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/invoke-rpc/invoke-rpc-module.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/invoke-rpc/invoke-rpc-module.yang index ba06645354..208c2164d5 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/resources/invoke-rpc/invoke-rpc-module.yang +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/invoke-rpc/invoke-rpc-module.yang @@ -16,5 +16,8 @@ module invoke-rpc-module { } } } - + + rpc rpc-noop { + } + } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-routed-rpc.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-routed-rpc.yang new file mode 100644 index 0000000000..c3a982473c --- /dev/null +++ b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-test-routed-rpc.yang @@ -0,0 +1,46 @@ +module opendaylight-test-routed-rpc { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:rpc:routing"; + prefix "rpc"; + import yang-ext { prefix ext; } + + description + "Test model for testing of registering rpc service on binding independent mount point + and retrieving rpc service via binding aware mount point."; + + revision "2014-07-01" { + description + "Initial revision"; + } + + identity test-context { + description "Test Context"; + } + + typedef encapsulated-route { + type instance-identifier; + } + + grouping route-in-grouping { + leaf route { + type instance-identifier; + ext:context-reference test-context; + } + } + + grouping encapsulated-route-in-grouping { + leaf route { + type encapsulated-route; + ext:context-reference test-context; + } + } + + rpc routed-simple-route { + input { + leaf route { + type instance-identifier; + ext:context-reference test-context; + } + } + } +} diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java index 50ae8fd04f..c6aa545935 100644 --- a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java +++ b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java @@ -1,15 +1,12 @@ package org.opendaylight.controller.sample.kitchen.impl; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; + import org.opendaylight.controller.config.yang.config.kitchen_service.impl.KitchenServiceRuntimeMXBean; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sample.kitchen.api.EggsType; import org.opendaylight.controller.sample.kitchen.api.KitchenService; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput; @@ -20,10 +17,10 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WheatBread; -import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,7 +86,8 @@ public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntime } return Futures.immediateFuture( - Rpcs. getRpcResult( atLeastOneSucceeded, errorList.build() ) ); + RpcResultBuilder. status( atLeastOneSucceeded ) + .withRpcErrors( errorList.build() ).build() ); } } ); } @@ -102,7 +100,7 @@ public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntime public RpcResult call() throws Exception { // We don't actually do anything here - just return a successful result. - return Rpcs. getRpcResult( true, Collections.emptyList() ); + return RpcResultBuilder. success().build(); } } ); } @@ -113,11 +111,9 @@ public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntime if( toasterOutOfBread ) { log.info( "We're out of toast but we can make eggs" ); - return Futures.immediateFuture( Rpcs. getRpcResult( true, - Arrays.asList( RpcErrors.getRpcError( "", "partial-operation", null, - ErrorSeverity.WARNING, - "Toaster is out of bread but we can make you eggs", - ErrorType.APPLICATION, null ) ) ) ); + return Futures.immediateFuture( RpcResultBuilder. success() + .withWarning( ErrorType.APPLICATION, "partial-operation", + "Toaster is out of bread but we can make you eggs" ).build() ); } // Access the ToasterService to make the toast. diff --git a/opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java b/opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java index d2b0f90194..b7518e094d 100644 --- a/opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java +++ b/opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java @@ -7,9 +7,6 @@ */ package org.opendaylight.controller.sample.toaster.provider; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -23,13 +20,11 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; -import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; -import org.opendaylight.controller.sal.common.util.RpcErrors; -import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput; @@ -43,7 +38,7 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120 import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcError; -import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; @@ -105,24 +100,23 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti executor.shutdown(); if (dataProvider != null) { - WriteTransaction t = dataProvider.newWriteOnlyTransaction(); - t.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID); - ListenableFuture> future = t.commit(); - Futures.addCallback( future, new FutureCallback>() { + WriteTransaction tx = dataProvider.newWriteOnlyTransaction(); + tx.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID); + Futures.addCallback( tx.submit(), new FutureCallback() { @Override - public void onSuccess( RpcResult result ) { + public void onSuccess( final Void result ) { LOG.debug( "Delete Toaster commit result: " + result ); } @Override - public void onFailure( Throwable t ) { + public void onFailure( final Throwable t ) { LOG.error( "Delete of Toaster failed", t ); } } ); } } - private Toaster buildToaster( ToasterStatus status ) { + private Toaster buildToaster( final ToasterStatus status ) { // note - we are simulating a device whose manufacture and model are // fixed (embedded) into the hardware. @@ -163,8 +157,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti } // Always return success from the cancel toast call. - return Futures.immediateFuture( Rpcs. getRpcResult( true, - Collections.emptyList() ) ); + return Futures.immediateFuture( RpcResultBuilder. success().build() ); } /** @@ -176,46 +169,43 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti final SettableFuture> futureResult = SettableFuture.create(); - checkStatusAndMakeToast( input, futureResult ); + checkStatusAndMakeToast( input, futureResult, 2 ); return futureResult; } - private List makeToasterOutOfBreadError() { - return Arrays.asList( - RpcErrors.getRpcError( "out-of-stock", "resource-denied", null, null, - "Toaster is out of bread", - ErrorType.APPLICATION, null ) ); + private RpcError makeToasterOutOfBreadError() { + return RpcResultBuilder.newError( ErrorType.APPLICATION, "resource-denied", + "Toaster is out of bread", "out-of-stock", null, null ); } - private List makeToasterInUseError() { - return Arrays.asList( - RpcErrors.getRpcError( "", "in-use", null, ErrorSeverity.WARNING, - "Toaster is busy", ErrorType.APPLICATION, null ) ); + private RpcError makeToasterInUseError() { + return RpcResultBuilder.newWarning( ErrorType.APPLICATION, "in-use", + "Toaster is busy", null, null, null ); } private void checkStatusAndMakeToast( final MakeToastInput input, - final SettableFuture> futureResult ) { + final SettableFuture> futureResult, + final int tries ) { // Read the ToasterStatus and, if currently Up, try to write the status to Down. // If that succeeds, then we essentially have an exclusive lock and can proceed // to make toast. final ReadWriteTransaction tx = dataProvider.newReadWriteTransaction(); - ListenableFuture> readFuture = + ListenableFuture> readFuture = tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID ); - final ListenableFuture> commitFuture = - Futures.transform( readFuture, new AsyncFunction, - RpcResult>() { + final ListenableFuture commitFuture = + Futures.transform( readFuture, new AsyncFunction,Void>() { @Override - public ListenableFuture> apply( - Optional toasterData ) throws Exception { + public ListenableFuture apply( + final Optional toasterData ) throws Exception { ToasterStatus toasterStatus = ToasterStatus.Up; if( toasterData.isPresent() ) { - toasterStatus = ((Toaster)toasterData.get()).getToasterStatus(); + toasterStatus = toasterData.get().getToasterStatus(); } LOG.debug( "Read toaster status: {}", toasterStatus ); @@ -225,8 +215,8 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti if( outOfBread() ) { LOG.debug( "Toaster is out of bread" ); - return Futures.immediateFuture( Rpcs.getRpcResult( - false, null, makeToasterOutOfBreadError() ) ); + return Futures.immediateFailedCheckedFuture( + new TransactionCommitFailedException( "", makeToasterOutOfBreadError() ) ); } LOG.debug( "Setting Toaster status to Down" ); @@ -236,7 +226,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti // concurrent toasting. tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID, buildToaster( ToasterStatus.Down ) ); - return tx.commit(); + return tx.submit(); } LOG.debug( "Oops - already making toast!" ); @@ -244,52 +234,44 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti // Return an error since we are already making toast. This will get // propagated to the commitFuture below which will interpret the null // TransactionStatus in the RpcResult as an error condition. - return Futures.immediateFuture( Rpcs.getRpcResult( - false, null, makeToasterInUseError() ) ); + return Futures.immediateFailedCheckedFuture( + new TransactionCommitFailedException( "", makeToasterInUseError() ) ); } } ); - Futures.addCallback( commitFuture, new FutureCallback>() { + Futures.addCallback( commitFuture, new FutureCallback() { @Override - public void onSuccess( RpcResult result ) { - if( result.getResult() == TransactionStatus.COMMITED ) { - - // OK to make toast - currentMakeToastTask.set( executor.submit( - new MakeToastTask( input, futureResult ) ) ); - } else { - - LOG.debug( "Setting error result" ); - - // Either the transaction failed to commit for some reason or, more likely, - // the read above returned ToasterStatus.Down. Either way, fail the - // futureResult and copy the errors. - - futureResult.set( Rpcs.getRpcResult( false, null, result.getErrors() ) ); - } + public void onSuccess( final Void result ) { + // OK to make toast + currentMakeToastTask.set( executor.submit( new MakeToastTask( input, futureResult ) ) ); } @Override - public void onFailure( Throwable ex ) { + public void onFailure( final Throwable ex ) { if( ex instanceof OptimisticLockFailedException ) { // Another thread is likely trying to make toast simultaneously and updated the // status before us. Try reading the status again - if another make toast is // now in progress, we should get ToasterStatus.Down and fail. - LOG.debug( "Got OptimisticLockFailedException - trying again" ); + if( ( tries - 1 ) > 0 ) { + LOG.debug( "Got OptimisticLockFailedException - trying again" ); - checkStatusAndMakeToast( input, futureResult ); + checkStatusAndMakeToast( input, futureResult, tries - 1 ); + } + else { + futureResult.set( RpcResultBuilder. failed() + .withError( ErrorType.APPLICATION, ex.getMessage() ).build() ); + } } else { - LOG.error( "Failed to commit Toaster status", ex ); + LOG.debug( "Failed to commit Toaster status", ex ); - // Got some unexpected error so fail. - futureResult.set( Rpcs. getRpcResult( false, null, Arrays.asList( - RpcErrors.getRpcError( null, null, null, ErrorSeverity.ERROR, - ex.getMessage(), - ErrorType.APPLICATION, ex ) ) ) ); + // Probably already making toast. + futureResult.set( RpcResultBuilder. failed() + .withRpcErrors( ((TransactionCommitFailedException)ex).getErrorList() ) + .build() ); } } } ); @@ -312,7 +294,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti notificationProvider.publish( reStockedNotification ); } - return Futures.immediateFuture(Rpcs. getRpcResult(true, Collections.emptyList())); + return Futures.immediateFuture( RpcResultBuilder. success().build() ); } /** @@ -337,20 +319,14 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti WriteTransaction tx = dataProvider.newWriteOnlyTransaction(); tx.put( LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster( ToasterStatus.Up ) ); - ListenableFuture> commitFuture = tx.commit(); - - Futures.addCallback( commitFuture, new FutureCallback>() { + Futures.addCallback( tx.submit(), new FutureCallback() { @Override - public void onSuccess( RpcResult result ) { - if( result.getResult() != TransactionStatus.COMMITED ) { - LOG.error( "Failed to update toaster status: " + result.getErrors() ); - } - - notifyCallback( result.getResult() == TransactionStatus.COMMITED ); + public void onSuccess( final Void result ) { + notifyCallback( true ); } @Override - public void onFailure( Throwable t ) { + public void onFailure( final Throwable t ) { // We shouldn't get an OptimisticLockFailedException (or any ex) as no // other component should be updating the operational state. LOG.error( "Failed to update toaster status", t ); @@ -358,7 +334,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti notifyCallback( false ); } - void notifyCallback( boolean result ) { + void notifyCallback( final boolean result ) { if( resultCallback != null ) { resultCallback.apply( result ); } @@ -410,14 +386,13 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti setToasterStatusUp( new Function() { @Override - public Void apply( Boolean result ) { + public Void apply( final Boolean result ) { currentMakeToastTask.set( null ); LOG.debug("Toast done"); - futureResult.set( Rpcs.getRpcResult( true, null, - Collections.emptyList() ) ); + futureResult.set( RpcResultBuilder.success().build() ); return null; } diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java index af61db1a80..b1db280c24 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java @@ -95,6 +95,8 @@ final class FlowComparator { if (statsFlow == storedFlow) { return true; } + if (storedFlow == null && statsFlow != null) return false; + if (statsFlow == null && storedFlow != null) return false; if (storedFlow.getClass() != statsFlow.getClass()) { return false; } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java index 5840c9dbd1..85cdf0335f 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceImpl.java @@ -9,33 +9,65 @@ package org.opendaylight.controller.netconf.confignetconfconnector.osgi; import java.lang.ref.SoftReference; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.concurrent.GuardedBy; +import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class YangStoreServiceImpl implements YangStoreService { + private static final Logger LOG = LoggerFactory.getLogger(YangStoreServiceImpl.class); + + /** + * This is a rather interesting locking model. We need to guard against both the + * cache expiring from GC and being invalidated by schema context change. The + * context can change while we are doing processing, so we do not want to block + * it, so no synchronization can happen on the methods. + * + * So what we are doing is the following: + * + * We synchronize with GC as usual, using a SoftReference. + * + * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when + * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now + * that may happen while the getter is already busy acting on the old schema context, + * so it needs to understand that a refresh has happened and retry. To do that, it + * attempts a CAS operation -- if it fails, in knows that the SoftReference has + * been replaced and thus it needs to retry. + * + * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally + * to stop multiple threads doing the same work. + */ + private final AtomicReference> ref = new AtomicReference<>(new SoftReference(null)); private final SchemaContextProvider service; - @GuardedBy("this") - private SoftReference cache = new SoftReference<>(null); - public YangStoreServiceImpl(SchemaContextProvider service) { + public YangStoreServiceImpl(final SchemaContextProvider service) { this.service = service; } @Override public synchronized YangStoreSnapshotImpl getYangStoreSnapshot() throws YangStoreException { - YangStoreSnapshotImpl yangStoreSnapshot = cache.get(); - if (yangStoreSnapshot == null) { - yangStoreSnapshot = new YangStoreSnapshotImpl(service.getSchemaContext()); - cache = new SoftReference<>(yangStoreSnapshot); + SoftReference r = ref.get(); + YangStoreSnapshotImpl ret = r.get(); + + while (ret == null) { + // We need to be compute a new value + ret = new YangStoreSnapshotImpl(service.getSchemaContext()); + + if (!ref.compareAndSet(r, new SoftReference<>(ret))) { + LOG.debug("Concurrent refresh detected, recomputing snapshot"); + r = ref.get(); + ret = null; + } } - return yangStoreSnapshot; + + return ret; } /** * Called when schema context changes, invalidates cache. */ - public synchronized void refresh() { - cache.clear(); + public void refresh() { + ref.set(new SoftReference(null)); } } diff --git a/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/commands/local/Disconnect.java b/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/commands/local/Disconnect.java index 723e484c9c..e9383886ea 100644 --- a/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/commands/local/Disconnect.java +++ b/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/commands/local/Disconnect.java @@ -7,7 +7,6 @@ */ package org.opendaylight.controller.netconf.cli.commands.local; -import com.google.common.collect.Lists; import org.opendaylight.controller.netconf.cli.NetconfDeviceConnectionManager; import org.opendaylight.controller.netconf.cli.commands.AbstractCommand; import org.opendaylight.controller.netconf.cli.commands.Command; @@ -21,6 +20,8 @@ import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import com.google.common.collect.Lists; + /** * Local disconnect command */ @@ -40,7 +41,7 @@ public class Disconnect extends AbstractCommand { connectionManager.disconnect(); return new Output(new CompositeNodeTOImpl(getCommandId(), null, - Lists.> newArrayList(new SimpleNodeTOImpl<>(new QName(getCommandId(), "status"), null, + Lists.> newArrayList(new SimpleNodeTOImpl<>(QName.create(getCommandId(), "status"), null, "Connection disconnected")))); } diff --git a/opendaylight/netconf/netconf-cli/src/test/java/org/opendaylight/controller/netconf/cli/io/IOUtilTest.java b/opendaylight/netconf/netconf-cli/src/test/java/org/opendaylight/controller/netconf/cli/io/IOUtilTest.java index 2021bf722c..5a2445279b 100644 --- a/opendaylight/netconf/netconf-cli/src/test/java/org/opendaylight/controller/netconf/cli/io/IOUtilTest.java +++ b/opendaylight/netconf/netconf-cli/src/test/java/org/opendaylight/controller/netconf/cli/io/IOUtilTest.java @@ -7,20 +7,23 @@ */ package org.opendaylight.controller.netconf.cli.io; -import com.google.common.collect.Maps; import java.util.Map; + import junit.framework.Assert; + import org.junit.Test; import org.opendaylight.controller.netconf.cli.commands.CommandConstants; import org.opendaylight.yangtools.yang.common.QName; +import com.google.common.collect.Maps; + public class IOUtilTest { @Test public void testQNameFromKeyStringNew() throws Exception { final String s = IOUtil.qNameToKeyString(CommandConstants.HELP_QNAME, "module"); final Map modulesMap = Maps.newHashMap(); - modulesMap.put("module", new QName(CommandConstants.HELP_QNAME, "module")); + modulesMap.put("module", QName.create(CommandConstants.HELP_QNAME, "module")); final QName qName = IOUtil.qNameFromKeyString(s, modulesMap); Assert.assertEquals(CommandConstants.HELP_QNAME, qName); } diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java index 829ac304bd..87b3f837e8 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/SshClientChannelInitializer.java @@ -11,9 +11,8 @@ import io.netty.channel.Channel; import io.netty.util.concurrent.Promise; import java.io.IOException; import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.SshHandler; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.Invoker; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler; import org.opendaylight.protocol.framework.SessionListenerFactory; final class SshClientChannelInitializer extends AbstractChannelInitializer { @@ -33,8 +32,7 @@ final class SshClientChannelInitializer extends AbstractChannelInitializer promise) { try { - final Invoker invoker = Invoker.subsystem("netconf"); - ch.pipeline().addFirst(new SshHandler(authenticationHandler, invoker)); + ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(authenticationHandler)); super.initialize(ch,promise); } catch (final IOException e) { throw new RuntimeException(e); diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java index afa17532d5..18ed18e4ae 100644 --- a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/test/TestingNetconfClient.java @@ -56,7 +56,7 @@ public class TestingNetconfClient implements Closeable { this.label = clientLabel; sessionListener = config.getSessionListener(); Future clientFuture = netconfClientDispatcher.createClient(config); - clientSession = get(clientFuture); + clientSession = get(clientFuture);//TODO: make static this.sessionId = clientSession.getSessionId(); } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionNegotiator.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionNegotiator.java index 1360a54d6f..de3f732b25 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionNegotiator.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionNegotiator.java @@ -11,6 +11,7 @@ package org.opendaylight.controller.netconf.nettyutil; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -104,7 +105,7 @@ extends AbstractSessionNegotiator { private void start() { final NetconfMessage helloMessage = this.sessionPreferences.getHelloMessage(); - logger.debug("Session negotiation started with hello message {}", XmlUtil.toString(helloMessage.getDocument())); + logger.debug("Session negotiation started with hello message {} on channel {}", XmlUtil.toString(helloMessage.getDocument()), channel); channel.pipeline().addLast(NAME_OF_EXCEPTION_HANDLER, new ExceptionHandlingInboundChannelHandler()); @@ -125,12 +126,20 @@ extends AbstractSessionNegotiator { // Do not fail negotiation if promise is done or canceled // It would result in setting result of the promise second time and that throws exception if (isPromiseFinished() == false) { - // FIXME BUG-1365 calling "negotiation failed" closes the channel, but the channel does not get closed if data is still being transferred - // Loopback connection initiation might negotiationFailed(new IllegalStateException("Session was not established after " + timeout)); + changeState(State.FAILED); + + channel.closeFuture().addListener(new GenericFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if(future.isSuccess()) { + logger.debug("Channel {} closed: success", future.channel()); + } else { + logger.warn("Channel {} closed: fail", future.channel()); + } + } + }); } - - changeState(State.FAILED); } else if(channel.isOpen()) { channel.pipeline().remove(NAME_OF_EXCEPTION_HANDLER); } @@ -214,9 +223,9 @@ extends AbstractSessionNegotiator { protected abstract S getSession(L sessionListener, Channel channel, NetconfHelloMessage message) throws NetconfDocumentedException; private synchronized void changeState(final State newState) { - logger.debug("Changing state from : {} to : {}", state, newState); - Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state, - newState); + logger.debug("Changing state from : {} to : {} for channel: {}", state, newState, channel); + Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s for chanel %s", state, + newState, channel); this.state = newState; } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEOMAggregator.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEOMAggregator.java index f260bcbcef..a87a08ded7 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEOMAggregator.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEOMAggregator.java @@ -9,56 +9,15 @@ package org.opendaylight.controller.netconf.nettyutil.handler; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import java.util.List; - +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; import org.opendaylight.controller.netconf.util.messages.NetconfMessageConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.google.common.base.Charsets; +public class NetconfEOMAggregator extends DelimiterBasedFrameDecoder { -public class NetconfEOMAggregator extends ByteToMessageDecoder { - private final static Logger logger = LoggerFactory.getLogger(NetconfEOMAggregator.class); + public static final ByteBuf DELIMITER = Unpooled.wrappedBuffer(NetconfMessageConstants.END_OF_MESSAGE); - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { - int index = indexOfSequence(in, NetconfMessageConstants.END_OF_MESSAGE); - if (index == -1) { - logger.debug("Message is not complete, read again."); - if (logger.isTraceEnabled()) { - String str = in.toString(Charsets.UTF_8); - logger.trace("Message read so far: {}", str); - } - ctx.read(); - } else { - ByteBuf msg = in.readBytes(index); - in.readBytes(NetconfMessageConstants.END_OF_MESSAGE.length); - in.discardReadBytes(); - logger.debug("Message is complete."); - out.add(msg); - } + public NetconfEOMAggregator() { + super(Integer.MAX_VALUE, DELIMITER); } - - private int indexOfSequence(ByteBuf in, byte[] sequence) { - int index = -1; - for (int i = 0; i < in.readableBytes() - sequence.length + 1; i++) { - if (in.getByte(i) == sequence[0]) { - index = i; - for (int j = 1; j < sequence.length; j++) { - if (in.getByte(i + j) != sequence[j]) { - index = -1; - break; - } - } - if (index != -1) { - return index; - } - } - } - return index; - } - } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToXMLEncoder.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToXMLEncoder.java index fae2000bb5..d810a870ff 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToXMLEncoder.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToXMLEncoder.java @@ -12,6 +12,7 @@ import io.netty.buffer.ByteBufOutputStream; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; +import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -61,7 +62,8 @@ public class NetconfMessageToXMLEncoder extends MessageToByteEncoder 0) { -/* - GrammarFactory grammarFactory = GrammarFactory.newInstance(); - if (operationElement - .getElementsByTagName(EXI_PARAMETER_SCHEMA_NONE) - .getLength() > 0) { - this.grammars = grammarFactory.createSchemaLessGrammars(); - } - - if (operationElement.getElementsByTagName( - EXI_PARAMETER_SCHEMA_BUILT_IN).getLength() > 0) { - this.grammars = grammarFactory.createXSDTypesOnlyGrammars(); - } - - if (operationElement.getElementsByTagName( - EXI_PARAMETER_SCHEMA_BASE_1_1).getLength() > 0) { - this.grammars = grammarFactory - .createGrammars(NETCONF_XSD_LOCATION); - } -*/ - - } - return new EXIParameters(options); } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/exi/NetconfStartExiMessage.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/exi/NetconfStartExiMessage.java index 1b0a34d7e0..72eb774b53 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/exi/NetconfStartExiMessage.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/exi/NetconfStartExiMessage.java @@ -48,7 +48,7 @@ public final class NetconfStartExiMessage extends NetconfMessage { Element startExiElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0, START_EXI); - addAlignemnt(exiOptions, doc, startExiElement); + addAlignment(exiOptions, doc, startExiElement); addFidelity(exiOptions, doc, startExiElement); rpcElement.appendChild(startExiElement); @@ -75,7 +75,7 @@ public final class NetconfStartExiMessage extends NetconfMessage { } } - private static void addAlignemnt(EXIOptions exiOptions, Document doc, Element startExiElement) { + private static void addAlignment(EXIOptions exiOptions, Document doc, Element startExiElement) { Element alignmentElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0, ALIGNMENT_KEY); alignmentElement.setTextContent(exiOptions.getAlignmentType().toString()); diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java index 67027d8014..b67aa0f96d 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java @@ -14,7 +14,7 @@ import java.io.IOException; /** * Class Providing username/password authentication option to - * {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.SshHandler} + * {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler} */ public class LoginPassword extends AuthenticationHandler { private final String username; diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java index d542e1952a..eab2546d6e 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java @@ -13,33 +13,38 @@ import java.io.IOException; * Abstract class providing mechanism of invoking various SSH level services. * Class is not allowed to be extended, as it provides its own implementations via instance initiators. */ -public abstract class Invoker { +abstract class Invoker { private boolean invoked = false; - private Invoker(){} + private Invoker() { + } protected boolean isInvoked() { - // TODO invoked is always false return invoked; } + public void setInvoked() { + this.invoked = true; + } + abstract void invoke(SshSession session) throws IOException; - /** - * Invoker implementation to invokes subsystem SSH service. - * - * @param subsystem - * @return - */ + public static Invoker netconfSubsystem(){ + return subsystem("netconf"); + } + public static Invoker subsystem(final String subsystem) { return new Invoker() { @Override - void invoke(SshSession session) throws IOException { + synchronized void invoke(SshSession session) throws IOException { if (isInvoked()) { throw new IllegalStateException("Already invoked."); } - - session.startSubSystem(subsystem); + try { + session.startSubSystem(subsystem); + } finally { + setInvoked(); + } } }; } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java index 3520fe029d..271b781b99 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java @@ -10,18 +10,16 @@ package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; -import ch.ethz.ssh2.channel.Channel; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket; - import java.io.IOException; import java.util.HashMap; import java.util.Map; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket; /** * Wrapper class around GANYMED SSH java library. */ -public class SshClient { +class SshClient { private final VirtualSocket socket; private final Map openSessions = new HashMap<>(); private final AuthenticationHandler authenticationHandler; @@ -51,15 +49,10 @@ public class SshClient { authenticationHandler.authenticate(connection); } - public void closeSession(SshSession session) { - if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) { - session.close(); - } - } public void close() { for (SshSession session : openSessions.values()){ - closeSession(session); + session.close(); } openSessions.clear(); @@ -68,4 +61,11 @@ public class SshClient { connection.close(); } } + + @Override + public String toString() { + return "SshClient{" + + "socket=" + socket + + '}'; + } } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java index ad8b25ff21..1a2eb3f1ab 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java @@ -8,8 +8,13 @@ package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import java.io.IOException; @@ -18,7 +23,6 @@ import java.io.OutputStream; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.atomic.AtomicBoolean; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocketException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +31,7 @@ import org.slf4j.LoggerFactory; * Worker thread class. Handles all downstream and upstream events in SSH Netty * pipeline. */ -public class SshClientAdapter implements Runnable { +class SshClientAdapter implements Runnable { private static final Logger logger = LoggerFactory.getLogger(SshClientAdapter.class); private static final int BUFFER_SIZE = 1024; @@ -51,6 +55,7 @@ public class SshClientAdapter implements Runnable { this.invoker = invoker; } + // TODO: refactor public void run() { try { SshSession session = sshClient.openSession(); @@ -80,12 +85,6 @@ public class SshClientAdapter implements Runnable { byteBuf.writeBytes(tranBuff); ctx.fireChannelRead(byteBuf); } - - } catch (VirtualSocketException e) { - // Netty closed connection prematurely. - // Or maybe tried to open ganymed connection without having initialized session - // (ctx.channel().remoteAddress() is null) - // Just pass and move on. } catch (Exception e) { logger.error("Unexpected exception", e); } finally { @@ -123,12 +122,23 @@ public class SshClientAdapter implements Runnable { } } - public void start(ChannelHandlerContext ctx) { - if (this.ctx != null) { - // context is already associated. - return; + public Thread start(ChannelHandlerContext ctx, ChannelFuture channelFuture) { + checkArgument(channelFuture.isSuccess()); + checkNotNull(ctx.channel().remoteAddress()); + synchronized (this) { + checkState(this.ctx == null); + this.ctx = ctx; } - this.ctx = ctx; - new Thread(this).start(); + String threadName = toString(); + Thread thread = new Thread(this, threadName); + thread.start(); + return thread; + } + + @Override + public String toString() { + return "SshClientAdapter{" + + "sshClient=" + sshClient + + '}'; } } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/SshHandler.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshHandler.java similarity index 83% rename from opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/SshHandler.java rename to opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshHandler.java index 1427c6f3a7..c710a010e2 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/SshHandler.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshHandler.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.netconf.nettyutil.handler.ssh; +package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; @@ -14,26 +14,30 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; - import java.io.IOException; import java.net.SocketAddress; - -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClient; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.Invoker; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClientAdapter; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Netty SSH handler class. Acts as interface between Netty and SSH library. All standard Netty message handling * stops at instance of this class. All downstream events are handed of to wrapped {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClientAdapter}; */ public class SshHandler extends ChannelOutboundHandlerAdapter { + private static final Logger logger = LoggerFactory.getLogger(SshHandler.class); private static final String SOCKET = "socket"; private final VirtualSocket virtualSocket = new VirtualSocket(); private final SshClientAdapter sshClientAdapter; + + public static SshHandler createForNetconfSubsystem(AuthenticationHandler authenticationHandler) throws IOException { + return new SshHandler(authenticationHandler, Invoker.netconfSubsystem()); + } + + public SshHandler(AuthenticationHandler authenticationHandler, Invoker invoker) throws IOException { SshClient sshClient = new SshClient(virtualSocket, authenticationHandler); this.sshClientAdapter = new SshClientAdapter(sshClient, invoker); @@ -67,7 +71,11 @@ public class SshHandler extends ChannelOutboundHandlerAdapter { promise.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture channelFuture) { - sshClientAdapter.start(ctx); + if (channelFuture.isSuccess()) { + sshClientAdapter.start(ctx, channelFuture); + } else { + logger.debug("Failed to connect to remote host"); + } }} ); } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java index 8311554cda..44893b8794 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java @@ -11,6 +11,7 @@ package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client; import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; +import ch.ethz.ssh2.channel.Channel; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -19,33 +20,18 @@ import java.io.OutputStream; /** * Wrapper class for proprietary SSH sessions implementations */ -public class SshSession implements Closeable { +class SshSession implements Closeable { private final Session session; public SshSession(Session session) { this.session = session; } - public void execCommand(String cmd) throws IOException { - session.execCommand(cmd); - } - - public void execCommand(String cmd, String charsetName) throws IOException { - session.execCommand(cmd, charsetName); - } - - public void startShell() throws IOException { - session.startShell(); - } public void startSubSystem(String name) throws IOException { session.startSubSystem(name); } - public int getState() { - return session.getState(); - } - public InputStream getStdout() { return new StreamGobbler(session.getStdout()); } @@ -58,24 +44,10 @@ public class SshSession implements Closeable { return session.getStdin(); } - public int waitUntilDataAvailable(long timeout) throws IOException { - return session.waitUntilDataAvailable(timeout); - } - - public int waitForCondition(int conditionSet, long timeout) { - return session.waitForCondition(conditionSet, timeout); - } - - public Integer getExitStatus() { - return session.getExitStatus(); - } - - public String getExitSignal() { - return session.getExitSignal(); - } - @Override public void close() { - session.close(); + if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) { + session.close(); + } } } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocket.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocket.java index 6debeba97e..69cce8057e 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocket.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocket.java @@ -25,32 +25,33 @@ import java.nio.channels.SocketChannel; * use OIO application in asynchronous environment and NIO EventLoop. Using VirtualSocket OIO applications * are able to use full potential of NIO environment. */ +//TODO: refactor - socket should be created when connection is established public class VirtualSocket extends Socket implements ChannelHandler { private static final String INPUT_STREAM = "inputStream"; private static final String OUTPUT_STREAM = "outputStream"; - private final ChannelInputStream chis = new ChannelInputStream(); - private final ChannelOutputStream chos = new ChannelOutputStream(); + private final ChannelInputStream chais = new ChannelInputStream(); + private final ChannelOutputStream chaos = new ChannelOutputStream(); private ChannelHandlerContext ctx; public InputStream getInputStream() { - return this.chis; + return this.chais; } public OutputStream getOutputStream() { - return this.chos; + return this.chaos; } public void handlerAdded(ChannelHandlerContext ctx) { this.ctx = ctx; if (ctx.channel().pipeline().get(OUTPUT_STREAM) == null) { - ctx.channel().pipeline().addFirst(OUTPUT_STREAM, chos); + ctx.channel().pipeline().addFirst(OUTPUT_STREAM, chaos); } if (ctx.channel().pipeline().get(INPUT_STREAM) == null) { - ctx.channel().pipeline().addFirst(INPUT_STREAM, chis); + ctx.channel().pipeline().addFirst(INPUT_STREAM, chais); } } @@ -69,7 +70,6 @@ public class VirtualSocket extends Socket implements ChannelHandler { ctx.fireExceptionCaught(throwable); } - public VirtualSocket() {super();} @Override public void connect(SocketAddress endpoint) throws IOException {} @@ -83,12 +83,7 @@ public class VirtualSocket extends Socket implements ChannelHandler { @Override public InetAddress getInetAddress() { InetSocketAddress isa = getInetSocketAddress(); - - if (isa == null) { - throw new VirtualSocketException(); - } - - return getInetSocketAddress().getAddress(); + return isa.getAddress(); } @Override @@ -187,7 +182,7 @@ public class VirtualSocket extends Socket implements ChannelHandler { @Override public String toString() { - return "Virtual socket InetAdress["+getInetAddress()+"], Port["+getPort()+"]"; + return "VirtualSocket{" + getInetAddress() + ":" + getPort() + "}"; } @Override diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocketException.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocketException.java deleted file mode 100644 index 626ebe937e..0000000000 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/virtualsocket/VirtualSocketException.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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.nettyutil.handler.ssh.virtualsocket; - -/** - * Exception class which provides notification about exceptional situations at the virtual socket layer. - */ -// FIXME: Switch to checked exception, create a runtime exception to workaround Socket API -public class VirtualSocketException extends RuntimeException { - private static final long serialVersionUID = 1L; -} diff --git a/opendaylight/netconf/netconf-ssh/pom.xml b/opendaylight/netconf/netconf-ssh/pom.xml index 8a2387d2c1..febf3abf8e 100644 --- a/opendaylight/netconf/netconf-ssh/pom.xml +++ b/opendaylight/netconf/netconf-ssh/pom.xml @@ -49,6 +49,16 @@ mockito-configuration test + + org.opendaylight.controller + netconf-netty-util + test + + + org.opendaylight.controller + netconf-client + test + diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java index 08bf9836b2..670f50ddd0 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java @@ -7,9 +7,11 @@ */ package org.opendaylight.controller.netconf.ssh; +import com.google.common.annotations.VisibleForTesting; import io.netty.channel.EventLoopGroup; import io.netty.channel.local.LocalAddress; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; @@ -68,6 +70,11 @@ public final class NetconfSSHServer extends Thread implements AutoCloseable { logger.trace("SSH server socket closed."); } + @VisibleForTesting + public InetSocketAddress getLocalSocketAddress() { + return (InetSocketAddress) serverSocket.getLocalSocketAddress(); + } + @Override public void run() { while (up) { diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java index 8045d32a50..6300c56e72 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java @@ -32,6 +32,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; import io.netty.handler.stream.ChunkedStream; +import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -119,14 +120,14 @@ public class Handshaker implements Runnable { class SSHClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(SSHClientHandler.class); private final AutoCloseable remoteConnection; - private final OutputStream remoteOutputStream; + private final BufferedOutputStream remoteOutputStream; private final String session; private ChannelHandlerContext channelHandlerContext; public SSHClientHandler(AutoCloseable remoteConnection, OutputStream remoteOutputStream, String session) { this.remoteConnection = remoteConnection; - this.remoteOutputStream = remoteOutputStream; + this.remoteOutputStream = new BufferedOutputStream(remoteOutputStream); this.session = session; } @@ -137,7 +138,7 @@ class SSHClientHandler extends ChannelInboundHandlerAdapter { } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { + public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException { ByteBuf bb = (ByteBuf) msg; // we can block the server here so that slow client does not cause memory pressure try { diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java index 5d0c71aa62..b768e2b1d1 100644 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClient.java @@ -25,29 +25,36 @@ import org.slf4j.LoggerFactory; * traffic between the echo client and server by sending the first message to * the server. */ -public class EchoClient implements Runnable { +public class EchoClient extends Thread { private static final Logger logger = LoggerFactory.getLogger(EchoClient.class); - private final ChannelHandler clientHandler; + private final ChannelInitializer channelInitializer; - public EchoClient(ChannelHandler clientHandler) { - this.clientHandler = clientHandler; + + public EchoClient(final ChannelHandler clientHandler) { + channelInitializer = new ChannelInitializer() { + @Override + public void initChannel(LocalChannel ch) throws Exception { + ch.pipeline().addLast(clientHandler); + } + }; + } + + public EchoClient(ChannelInitializer channelInitializer) { + this.channelInitializer = channelInitializer; } + @Override public void run() { // Configure the client. EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); + b.group(group) .channel(LocalChannel.class) - .handler(new ChannelInitializer() { - @Override - public void initChannel(LocalChannel ch) throws Exception { - ch.pipeline().addLast(clientHandler); - } - }); + .handler(channelInitializer); // Start the client. LocalAddress localAddress = new LocalAddress("foo"); diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java index 81182a580e..2a5791710a 100644 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/EchoClientHandler.java @@ -13,6 +13,8 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Charsets; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; @@ -23,31 +25,41 @@ import org.slf4j.LoggerFactory; * traffic between the echo client and server by sending the first message to * the server. */ -public class EchoClientHandler extends ChannelInboundHandlerAdapter { +public class EchoClientHandler extends ChannelInboundHandlerAdapter implements ChannelFutureListener { private static final Logger logger = LoggerFactory.getLogger(EchoClientHandler.class); private ChannelHandlerContext ctx; + private final StringBuilder fromServer = new StringBuilder(); + + public static enum State {CONNECTING, CONNECTED, FAILED_TO_CONNECT, CONNECTION_CLOSED} + + + private State state = State.CONNECTING; @Override - public void channelActive(ChannelHandlerContext ctx) { + public synchronized void channelActive(ChannelHandlerContext ctx) { checkState(this.ctx == null); - logger.info("client active"); + logger.info("channelActive"); this.ctx = ctx; + state = State.CONNECTED; } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - ByteBuf bb = (ByteBuf) msg; - logger.info(">{}", bb.toString(Charsets.UTF_8)); - bb.release(); + public synchronized void channelInactive(ChannelHandlerContext ctx) throws Exception { + state = State.CONNECTION_CLOSED; } @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf bb = (ByteBuf) msg; + String string = bb.toString(Charsets.UTF_8); + fromServer.append(string); + logger.info(">{}", string); + bb.release(); } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + public synchronized void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Close the connection when an exception is raised. logger.warn("Unexpected exception from downstream.", cause); checkState(this.ctx.equals(ctx)); @@ -55,8 +67,30 @@ public class EchoClientHandler extends ChannelInboundHandlerAdapter { this.ctx = null; } - public void write(String message) { + public synchronized void write(String message) { ByteBuf byteBuf = Unpooled.copiedBuffer(message.getBytes()); ctx.writeAndFlush(byteBuf); } + + public synchronized boolean isConnected() { + return state == State.CONNECTED; + } + + public synchronized String read() { + return fromServer.toString(); + } + + @Override + public synchronized void operationComplete(ChannelFuture future) throws Exception { + checkState(state == State.CONNECTING); + if (future.isSuccess()) { + logger.trace("Successfully connected, state will be switched in channelActive"); + } else { + state = State.FAILED_TO_CONNECT; + } + } + + public State getState() { + return state; + } } diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java index 2bda51b495..488c370145 100644 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java @@ -8,12 +8,28 @@ package org.opendaylight.controller.netconf.netty; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import com.google.common.base.Stopwatch; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.HashedWheelTimer; +import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.opendaylight.controller.netconf.netty.EchoClientHandler.State; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; @@ -23,6 +39,21 @@ import org.slf4j.LoggerFactory; public class SSHTest { public static final Logger logger = LoggerFactory.getLogger(SSHTest.class); + public static final String AHOJ = "ahoj\n"; + private EventLoopGroup nettyGroup; + HashedWheelTimer hashedWheelTimer; + + @Before + public void setUp() throws Exception { + hashedWheelTimer = new HashedWheelTimer(); + nettyGroup = new NioEventLoopGroup(); + } + + @After + public void tearDown() throws Exception { + hashedWheelTimer.stop(); + nettyGroup.shutdownGracefully(); + } @Test public void test() throws Exception { @@ -30,10 +61,63 @@ public class SSHTest { AuthProvider authProvider = mock(AuthProvider.class); doReturn(PEMGenerator.generate().toCharArray()).when(authProvider).getPEMAsCharArray(); doReturn(true).when(authProvider).authenticated(anyString(), anyString()); - NetconfSSHServer thread = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(), authProvider, new NioEventLoopGroup()); - Thread.sleep(2000); - logger.info("Closing socket"); - thread.close(); - thread.join(); + NetconfSSHServer netconfSSHServer = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(), + authProvider, new NioEventLoopGroup()); + + InetSocketAddress address = netconfSSHServer.getLocalSocketAddress(); + final EchoClientHandler echoClientHandler = connectClient(address); + Stopwatch stopwatch = new Stopwatch().start(); + while(echoClientHandler.isConnected() == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) { + Thread.sleep(100); + } + assertTrue(echoClientHandler.isConnected()); + logger.info("connected, writing to client"); + echoClientHandler.write(AHOJ); + // check that server sent back the same string + stopwatch = stopwatch.reset().start(); + while (echoClientHandler.read().endsWith(AHOJ) == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) { + Thread.sleep(100); + } + try { + String read = echoClientHandler.read(); + assertTrue(read + " should end with " + AHOJ, read.endsWith(AHOJ)); + } finally { + logger.info("Closing socket"); + netconfSSHServer.close(); + netconfSSHServer.join(); + } } + + public EchoClientHandler connectClient(InetSocketAddress address) { + final EchoClientHandler echoClientHandler = new EchoClientHandler(); + ChannelInitializer channelInitializer = new ChannelInitializer() { + @Override + public void initChannel(NioSocketChannel ch) throws Exception { + ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(new LoginPassword("a", "a"))); + ch.pipeline().addLast(echoClientHandler); + } + }; + Bootstrap b = new Bootstrap(); + + b.group(nettyGroup) + .channel(NioSocketChannel.class) + .handler(channelInitializer); + + // Start the client. + b.connect(address).addListener(echoClientHandler); + return echoClientHandler; + } + + @Test + public void testClientWithoutServer() throws Exception { + InetSocketAddress address = new InetSocketAddress(12345); + final EchoClientHandler echoClientHandler = connectClient(address); + Stopwatch stopwatch = new Stopwatch().start(); + while(echoClientHandler.getState() == State.CONNECTING && stopwatch.elapsed(TimeUnit.SECONDS) < 5) { + Thread.sleep(100); + } + assertFalse(echoClientHandler.isConnected()); + assertEquals(State.FAILED_TO_CONNECT, echoClientHandler.getState()); + } + } diff --git a/pom.xml b/pom.xml index af84002429..242ab8eb89 100644 --- a/pom.xml +++ b/pom.xml @@ -133,6 +133,7 @@ opendaylight/dummy-console opendaylight/karaf-branding opendaylight/distribution/opendaylight-karaf + features scm:git:ssh://git.opendaylight.org:29418/controller.git