From: Moiz Raja Date: Tue, 23 Dec 2014 18:12:30 +0000 (+0000) Subject: Merge "Bug 2517: Catch RuntimeExceptions thrown from the DCL in DataChangeListener" X-Git-Tag: release/lithium~740 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=e8ee8a8897013fb90376e21ccd9fc54a98a8c251;hp=75495f26e7718da7d6f9e0ad06af1caa2e3ebf08 Merge "Bug 2517: Catch RuntimeExceptions thrown from the DCL in DataChangeListener" --- diff --git a/features/neutron/pom.xml b/features/neutron/pom.xml index 59a0af2f89..642ce98351 100644 --- a/features/neutron/pom.xml +++ b/features/neutron/pom.xml @@ -16,10 +16,6 @@ - - org.opendaylight.controller - networkconfig.neutron - org.osgi org.osgi.core @@ -57,11 +53,6 @@ org.opendaylight.yangtools features-test - - org.opendaylight.yangtools - features-test - 0.7.0-SNAPSHOT - diff --git a/opendaylight/adsal/features/adsal/pom.xml b/opendaylight/adsal/features/adsal/pom.xml index 57fa1e2c3f..a13c2cb5f0 100644 --- a/opendaylight/adsal/features/adsal/pom.xml +++ b/opendaylight/adsal/features/adsal/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 org.opendaylight.controller diff --git a/opendaylight/adsal/features/base/pom.xml b/opendaylight/adsal/features/base/pom.xml index 795a323cc1..c5ea28080d 100644 --- a/opendaylight/adsal/features/base/pom.xml +++ b/opendaylight/adsal/features/base/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 org.opendaylight.controller diff --git a/opendaylight/adsal/features/controller/pom.xml b/opendaylight/adsal/features/controller/pom.xml index 34be32ff7f..9e41561a78 100644 --- a/opendaylight/adsal/features/controller/pom.xml +++ b/opendaylight/adsal/features/controller/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 org.opendaylight.controller diff --git a/opendaylight/adsal/features/extras/pom.xml b/opendaylight/adsal/features/extras/pom.xml index 103fbefad4..b86b0e28c3 100644 --- a/opendaylight/adsal/features/extras/pom.xml +++ b/opendaylight/adsal/features/extras/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 org.opendaylight.controller diff --git a/opendaylight/adsal/features/nsf/pom.xml b/opendaylight/adsal/features/nsf/pom.xml index 0c8e838009..19b3fe05da 100644 --- a/opendaylight/adsal/features/nsf/pom.xml +++ b/opendaylight/adsal/features/nsf/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 org.opendaylight.controller diff --git a/opendaylight/archetypes/odl-model-project/src/main/resources/archetype-resources/pom.xml b/opendaylight/archetypes/odl-model-project/src/main/resources/archetype-resources/pom.xml index 6b41485e7b..0786b366cd 100644 --- a/opendaylight/archetypes/odl-model-project/src/main/resources/archetype-resources/pom.xml +++ b/opendaylight/archetypes/odl-model-project/src/main/resources/archetype-resources/pom.xml @@ -1,225 +1,225 @@ - - - 4.0.0 - ${artifactId} - ${groupId} - ${version} - bundle - - UTF-8 - http://nexus.opendaylight.org/content + + + 4.0.0 + ${artifactId} + ${groupId} + ${version} + bundle + + UTF-8 + http://nexus.opendaylight.org/content opendaylight.release - opendaylight.release - 0.7.0-SNAPSHOT - 0.7.0-SNAPSHOT - 2.3.7 - - - scm:git:ssh://git.opendaylight.org:29418/controller.git - scm:git:ssh://git.opendaylight.org:29418/controller.git - https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main - - - - - - org.apache.felix - maven-bundle-plugin - ${bundle.plugin.version} - true - - - ${project.groupId}.${project.artifactId} - - ${project.basedir}/META-INF - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.5.1 - true - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.8.1 - - maven - - - - - aggregate - - site - - - - - org.opendaylight.yangtools - yang-maven-plugin - ${yang.version} - - - - generate-sources - - - src/main/yang - - - org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl - target/generated-sources/sal - - - false - - - - - - - org.opendaylight.yangtools - maven-sal-api-gen-plugin - ${yang.codegen.version} - jar - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.7 - - - generate-sources - - add-source - - - - target/generated-sources/sal - - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.opendaylight.yangtools - yang-maven-plugin - [0.5,) - - generate-sources - - - - - - - - - - - - - - - - - opendaylight-mirror - opendaylight-mirror - ${nexusproxy}/groups/public/ - - false - - - true - never - - - - - opendaylight-snapshot - opendaylight-snapshot + opendaylight.release + 0.7.0-SNAPSHOT + 0.7.0-SNAPSHOT + 2.3.7 + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + + + + + org.apache.felix + maven-bundle-plugin + ${bundle.plugin.version} + true + + + ${project.groupId}.${project.artifactId} + + ${project.basedir}/META-INF + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + true + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + maven + + + + + aggregate + + site + + + + + org.opendaylight.yangtools + yang-maven-plugin + ${yang.version} + + + + generate-sources + + + src/main/yang + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + target/generated-sources/sal + + + false + + + + + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + ${yang.codegen.version} + jar + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + generate-sources + + add-source + + + + target/generated-sources/sal + + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.opendaylight.yangtools + yang-maven-plugin + [0.5,) + + generate-sources + + + + + + + + + + + + + + + + + opendaylight-mirror + opendaylight-mirror + ${nexusproxy}/groups/public/ + + false + + + true + never + + + + + opendaylight-snapshot + opendaylight-snapshot ${nexusproxy}/repositories/${nexus.repository.snapshot}/ - - true - - - false - - - - - - - - opendaylight-mirror - opendaylight-mirror - ${nexusproxy}/groups/public/ - - false - - - true - never - - - - - opendaylight-snapshot - opendaylight-snapshot + + true + + + false + + + + + + + + opendaylight-mirror + opendaylight-mirror + ${nexusproxy}/groups/public/ + + false + + + true + never + + + + + opendaylight-snapshot + opendaylight-snapshot ${nexusproxy}/repositories/${nexus.repository.snapshot}/ - - true - - - false - - - - - - - - opendaylight-release + + true + + + false + + + + + + + + opendaylight-release ${nexusproxy}/repositories/${nexus.repository.release}/ - - - - opendaylight-snapshot + + + + opendaylight-snapshot ${nexusproxy}/repositories/${nexus.repository.snapshot}/ - - - - website - ${sitedeploy} - - - - - org.opendaylight.yangtools - yang-binding - ${yang.codegen.version} - - - + + + + website + ${sitedeploy} + + + + + org.opendaylight.yangtools + yang-binding + ${yang.codegen.version} + + + diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 8596412de8..84c04e0ebf 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -181,7 +181,6 @@ target/code-coverage/jacoco.exec target/code-coverage/jacoco-it.exec org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages - Sonar way with Findbugs 1.0.0 1.2.1 3.1.4.RELEASE diff --git a/opendaylight/config/config-persister-directory-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/xml/XmlDirectoryPersister.java b/opendaylight/config/config-persister-directory-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/xml/XmlDirectoryPersister.java index 85f70b9a01..3ea432e173 100644 --- a/opendaylight/config/config-persister-directory-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/xml/XmlDirectoryPersister.java +++ b/opendaylight/config/config-persister-directory-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/directory/xml/XmlDirectoryPersister.java @@ -23,6 +23,10 @@ import java.util.SortedSet; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.storage.file.xml.model.ConfigSnapshot; @@ -105,8 +109,15 @@ public class XmlDirectoryPersister implements Persister { public static ConfigSnapshotHolder loadLastConfig(final File file) throws JAXBException { JAXBContext jaxbContext = JAXBContext.newInstance(ConfigSnapshot.class); Unmarshaller um = jaxbContext.createUnmarshaller(); - - return asHolder((ConfigSnapshot) um.unmarshal(file)); + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + try { + XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(file)); + return asHolder((ConfigSnapshot) um.unmarshal(xsr)); + } catch (final XMLStreamException e) { + throw new JAXBException(e); + } } private static ConfigSnapshotHolder asHolder(final ConfigSnapshot unmarshalled) { diff --git a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java index 1bce5f2364..518716cfa7 100644 --- a/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java +++ b/opendaylight/config/config-persister-feature-adapter/src/main/java/org/opendaylight/controller/configpusherfeature/internal/FeatureConfigSnapshotHolder.java @@ -20,6 +20,10 @@ import java.util.SortedSet; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; import org.apache.karaf.features.ConfigFileInfo; import org.apache.karaf.features.Feature; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; @@ -59,10 +63,18 @@ public class FeatureConfigSnapshotHolder implements ConfigSnapshotHolder { Preconditions.checkNotNull(feature); this.fileInfo = fileInfo; this.featureChain.add(feature); + // TODO extract utility method for umarshalling config snapshots JAXBContext jaxbContext = JAXBContext.newInstance(ConfigSnapshot.class); Unmarshaller um = jaxbContext.createUnmarshaller(); - File file = new File(fileInfo.getFinalname()); - unmarshalled = ((ConfigSnapshot) um.unmarshal(file)); + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + try { + XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(new File(fileInfo.getFinalname()))); + unmarshalled = ((ConfigSnapshot) um.unmarshal(xsr)); + } catch (final XMLStreamException e) { + throw new JAXBException(e); + } } /* * (non-Javadoc) diff --git a/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/Config.java b/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/Config.java index e629d20db5..6a6d360cfa 100644 --- a/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/Config.java +++ b/opendaylight/config/config-persister-file-xml-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/xml/model/Config.java @@ -22,6 +22,10 @@ import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; import org.apache.commons.lang3.StringUtils; @XmlRootElement(name = "persisted-snapshots") @@ -72,9 +76,12 @@ public final class Config { try { JAXBContext jaxbContext = JAXBContext.newInstance(Config.class); Unmarshaller um = jaxbContext.createUnmarshaller(); - - return (Config) um.unmarshal(from); - } catch (JAXBException e) { + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(from)); + return ((Config) um.unmarshal(xsr)); + } catch (JAXBException | XMLStreamException e) { throw new PersistException("Unable to restore configuration", e); } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java index b1c73f6f41..cc2e55d51b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java @@ -45,8 +45,35 @@ public class Follower extends AbstractRaftActorBehavior { scheduleElection(electionDuration()); } + private boolean isLogEntryPresent(long index){ + if(index == context.getReplicatedLog().getSnapshotIndex()){ + return true; + } + + ReplicatedLogEntry previousEntry = context.getReplicatedLog() + .get(index); + + return previousEntry != null; + + } + + private long getLogEntryTerm(long index){ + if(index == context.getReplicatedLog().getSnapshotIndex()){ + return context.getReplicatedLog().getSnapshotTerm(); + } + + ReplicatedLogEntry previousEntry = context.getReplicatedLog() + .get(index); + + if(previousEntry != null){ + return previousEntry.getTerm(); + } + + return -1; + } + @Override protected RaftActorBehavior handleAppendEntries(ActorRef sender, - AppendEntries appendEntries) { + AppendEntries appendEntries) { if(appendEntries.getEntries() != null && appendEntries.getEntries().size() > 0) { if(LOG.isDebugEnabled()) { @@ -67,15 +94,15 @@ public class Follower extends AbstractRaftActorBehavior { // 2. Reply false if log doesn’t contain an entry at prevLogIndex // whose term matches prevLogTerm (§5.3) - ReplicatedLogEntry previousEntry = context.getReplicatedLog() - .get(appendEntries.getPrevLogIndex()); + long prevLogTerm = getLogEntryTerm(appendEntries.getPrevLogIndex()); + boolean prevEntryPresent = isLogEntryPresent(appendEntries.getPrevLogIndex()); boolean outOfSync = true; // First check if the logs are in sync or not if (lastIndex() == -1 - && appendEntries.getPrevLogIndex() != -1) { + && appendEntries.getPrevLogIndex() != -1) { // The follower's log is out of sync because the leader does have // an entry at prevLogIndex and this follower has no entries in @@ -83,34 +110,34 @@ public class Follower extends AbstractRaftActorBehavior { if(LOG.isDebugEnabled()) { LOG.debug("The followers log is empty and the senders prevLogIndex is {}", - appendEntries.getPrevLogIndex()); + appendEntries.getPrevLogIndex()); } } else if (lastIndex() > -1 - && appendEntries.getPrevLogIndex() != -1 - && previousEntry == null) { + && appendEntries.getPrevLogIndex() != -1 + && !prevEntryPresent) { // The follower's log is out of sync because the Leader's // prevLogIndex entry was not found in it's log if(LOG.isDebugEnabled()) { LOG.debug("The log is not empty but the prevLogIndex {} was not found in it", - appendEntries.getPrevLogIndex()); + appendEntries.getPrevLogIndex()); } } else if (lastIndex() > -1 - && previousEntry != null - && previousEntry.getTerm()!= appendEntries.getPrevLogTerm()) { + && prevEntryPresent + && prevLogTerm != appendEntries.getPrevLogTerm()) { // The follower's log is out of sync because the Leader's // prevLogIndex entry does exist in the follower's log but it has // a different term in it - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug( - "Cannot append entries because previous entry term {} is not equal to append entries prevLogTerm {}" - , previousEntry.getTerm() - , appendEntries.getPrevLogTerm()); + "Cannot append entries because previous entry term {} is not equal to append entries prevLogTerm {}" + , prevLogTerm + , appendEntries.getPrevLogTerm()); } } else { outOfSync = false; @@ -120,9 +147,9 @@ public class Follower extends AbstractRaftActorBehavior { // We found that the log was out of sync so just send a negative // reply and return if(LOG.isDebugEnabled()) { - LOG.debug("Follower is out-of-sync, " + + LOG.debug("Follower ({}) is out-of-sync, " + "so sending negative reply, lastIndex():{}, lastTerm():{}", - lastIndex(), lastTerm() + context.getId(), lastIndex(), lastTerm() ); } sender.tell( diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java index 0ee9693d32..a04d6aeb55 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java @@ -421,6 +421,119 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { }}; } + @Test + public void testHandleAppendEntriesPreviousLogEntryMissing(){ + new JavaTestKit(getSystem()) {{ + + MockRaftActorContext context = (MockRaftActorContext) + createActorContext(); + + // Prepare the receivers log + MockRaftActorContext.SimpleReplicatedLog log = + new MockRaftActorContext.SimpleReplicatedLog(); + log.append( + new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero"))); + log.append( + new MockRaftActorContext.MockReplicatedLogEntry(1, 1, new MockRaftActorContext.MockPayload("one"))); + log.append( + new MockRaftActorContext.MockReplicatedLogEntry(1, 2, new MockRaftActorContext.MockPayload("two"))); + + context.setReplicatedLog(log); + + // Prepare the entries to be sent with AppendEntries + List entries = new ArrayList<>(); + entries.add( + new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1"))); + + AppendEntries appendEntries = + new AppendEntries(1, "leader-1", 3, 1, entries, 4); + + RaftActorBehavior behavior = createBehavior(context); + + // Send an unknown message so that the state of the RaftActor remains unchanged + RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown"); + + RaftActorBehavior raftBehavior = + behavior.handleMessage(getRef(), appendEntries); + + assertEquals(expected, raftBehavior); + + // Also expect an AppendEntriesReply to be sent where success is false + final Boolean out = new ExpectMsg(duration("1 seconds"), + "AppendEntriesReply") { + // do not put code outside this method, will run afterwards + protected Boolean match(Object in) { + if (in instanceof AppendEntriesReply) { + AppendEntriesReply reply = (AppendEntriesReply) in; + return reply.isSuccess(); + } else { + throw noMatch(); + } + } + }.get(); + + assertEquals(false, out); + + }}; + + } + + @Test + public void testHandleAppendAfterInstallingSnapshot(){ + new JavaTestKit(getSystem()) {{ + + MockRaftActorContext context = (MockRaftActorContext) + createActorContext(); + + + // Prepare the receivers log + MockRaftActorContext.SimpleReplicatedLog log = + new MockRaftActorContext.SimpleReplicatedLog(); + + // Set up a log as if it has been snapshotted + log.setSnapshotIndex(3); + log.setSnapshotTerm(1); + + context.setReplicatedLog(log); + + // Prepare the entries to be sent with AppendEntries + List entries = new ArrayList<>(); + entries.add( + new MockRaftActorContext.MockReplicatedLogEntry(1, 4, new MockRaftActorContext.MockPayload("two-1"))); + + AppendEntries appendEntries = + new AppendEntries(1, "leader-1", 3, 1, entries, 4); + + RaftActorBehavior behavior = createBehavior(context); + + // Send an unknown message so that the state of the RaftActor remains unchanged + RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown"); + + RaftActorBehavior raftBehavior = + behavior.handleMessage(getRef(), appendEntries); + + assertEquals(expected, raftBehavior); + + // Also expect an AppendEntriesReply to be sent where success is false + final Boolean out = new ExpectMsg(duration("1 seconds"), + "AppendEntriesReply") { + // do not put code outside this method, will run afterwards + protected Boolean match(Object in) { + if (in instanceof AppendEntriesReply) { + AppendEntriesReply reply = (AppendEntriesReply) in; + return reply.isSuccess(); + } else { + throw noMatch(); + } + } + }.get(); + + assertEquals(true, out); + + }}; + + } + /** * This test verifies that when InstallSnapshot is received by 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 6977588a01..116491b518 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 @@ -13,14 +13,14 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; - import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -35,9 +35,6 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; - public class RpcInvocationStrategyTest { @Mock @@ -68,11 +65,11 @@ public class RpcInvocationStrategyTest { public class MockRpcService implements RpcService { - public Future rpcnameWithInputNoOutput(DataObject input) { + public Future rpcnameWithInputNoOutput(final DataObject input) { return futureDataObj; } - public Future> rpcnameWithInputWithOutput(DataObject input) { + public Future> rpcnameWithInputWithOutput(final DataObject input) { return futureDataObj; } @@ -94,7 +91,7 @@ public class RpcInvocationStrategyTest { urn = new URI(new String("urn:a:valid:urn")); } - private void setupForForwardToDom(boolean hasOutput, boolean hasInput, int expectedErrorSize) { + private void setupForForwardToDom(final boolean hasOutput, final boolean hasInput, final int expectedErrorSize) { if (expectedErrorSize > 0) { errors.add(rpcError); @@ -117,8 +114,8 @@ public class RpcInvocationStrategyTest { } - private void validateForwardToDomBroker(ListenableFuture> forwardToDomBroker, - boolean expectedSuccess, DataObject expectedResult, int expectedErrorSize) + private void validateForwardToDomBroker(final ListenableFuture> forwardToDomBroker, + final boolean expectedSuccess, final DataObject expectedResult, final int expectedErrorSize) throws InterruptedException, ExecutionException { assertNotNull(forwardToDomBroker); assertEquals(expectedSuccess, forwardToDomBroker.get().isSuccessful()); @@ -126,9 +123,9 @@ public class RpcInvocationStrategyTest { assertEquals(expectedErrorSize, forwardToDomBroker.get().getErrors().size()); } - private void setupTestMethod(String rpcName, String testMethodName, boolean hasInput) + private void setupTestMethod(final String rpcName, final String testMethodName, final boolean hasInput) throws NoSuchMethodException { - mockQName = new QName(urn, new Date(0L), new String("prefix"), new String(rpcName)); + mockQName = QName.create(urn, new Date(0L), new String(rpcName)); java.lang.reflect.Method rpcMethod = hasInput ? MockRpcService.class.getMethod(rpcName, DataObject.class) : MockRpcService.class.getMethod(rpcName); rpcInvocationStrategy = new RpcInvocationStrategy(mockQName, rpcMethod, mockMappingService, @@ -192,7 +189,7 @@ public class RpcInvocationStrategyTest { /* * invokeOn Tests */ - private void setupRpcResultsWithOutput(int expectedErrorSize) { + private void setupRpcResultsWithOutput(final int expectedErrorSize) { if (expectedErrorSize > 0) { errors.add(rpcError); } @@ -206,7 +203,7 @@ public class RpcInvocationStrategyTest { when(mockMappingService.toDataDom(toDataDomInput)).thenReturn(outputInvokeOn); } - private void setupRpcResultsNoOutput(int expectedErrorSize) { + private void setupRpcResultsNoOutput(final int expectedErrorSize) { if (expectedErrorSize > 0) { errors.add(rpcError); } @@ -219,8 +216,8 @@ public class RpcInvocationStrategyTest { } private void validateReturnedImmediateFuture( - ListenableFuture> immediateFuture, boolean expectedSuccess, - CompositeNode expectedReturn, int expectedErrorSize) throws InterruptedException, + final ListenableFuture> immediateFuture, final boolean expectedSuccess, + final CompositeNode expectedReturn, final int expectedErrorSize) throws InterruptedException, ExecutionException { assertNotNull(immediateFuture); assertEquals(expectedSuccess, immediateFuture.get().isSuccessful()); diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/CompositeModificationByteStringPayload.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/CompositeModificationByteStringPayload.java new file mode 100644 index 0000000000..99de5dde35 --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/CompositeModificationByteStringPayload.java @@ -0,0 +1,119 @@ +/* + * 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.cluster.raft.protobuff.client.messages; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.protobuf.ByteString; +import com.google.protobuf.GeneratedMessage; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.UnknownFieldSet; +import java.io.IOException; +import java.io.Serializable; +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.Map; +import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages; +import org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CompositeModificationByteStringPayload extends Payload implements + Serializable { + private static final long serialVersionUID = 1L; + + private ByteString byteString; + private SoftReference modificationReference; + private static final Logger LOG = LoggerFactory.getLogger(CompositeModificationByteStringPayload.class); + + public CompositeModificationByteStringPayload(){ + byteString = null; + } + public CompositeModificationByteStringPayload(Object modification){ + this(((PersistentMessages.CompositeModification) modification).toByteString()); + this.modificationReference = new SoftReference<>((PersistentMessages.CompositeModification) modification); + } + + private CompositeModificationByteStringPayload(ByteString byteString){ + this.byteString = Preconditions.checkNotNull(byteString, "byteString should not be null"); + } + + + @Override + public Map encode() { + Preconditions.checkState(byteString!=null); + Map map = new HashMap<>(); + map.put(org.opendaylight.controller.protobuff.messages.shard.CompositeModificationPayload.modification, + getModificationInternal()); + return map; + } + + @Override + public Payload decode( + AppendEntriesMessages.AppendEntries.ReplicatedLogEntry.Payload payload) { + PersistentMessages.CompositeModification modification = payload + .getExtension( + org.opendaylight.controller.protobuff.messages.shard.CompositeModificationPayload.modification); + + // The extension was put in the unknown field. + // This is because extensions need to be registered + // see org.opendaylight.controller.mdsal.CompositeModificationPayload.registerAllExtensions + // also see https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/ExtensionRegistry + // If that is not done then on the other end the extension shows up as an unknown field + // Need to figure out a better way to do this + if(payload.getUnknownFields().hasField(2)){ + UnknownFieldSet.Field field = + payload.getUnknownFields().getField(2); + + return new CompositeModificationByteStringPayload(field.getLengthDelimitedList().get(0)); + } + + return new CompositeModificationByteStringPayload(modification); + } + + public Object getModification(){ + return getModificationInternal(); + } + + private PersistentMessages.CompositeModification getModificationInternal(){ + if(this.modificationReference != null && this.modificationReference.get() != null){ + return this.modificationReference.get(); + } + try { + PersistentMessages.CompositeModification compositeModification = PersistentMessages.CompositeModification.parseFrom(this.byteString); + this.modificationReference = new SoftReference<>(compositeModification); + return compositeModification; + } catch (InvalidProtocolBufferException e) { + LOG.error("Unexpected exception occurred when parsing byteString to CompositeModification", e); + } + + return null; + } + + public int size(){ + return byteString.size(); + } + + private void writeObject(java.io.ObjectOutputStream stream) + throws IOException { + byteString.writeTo(stream); + } + + private void readObject(java.io.ObjectInputStream stream) + throws IOException, ClassNotFoundException { + byteString = ByteString.readFrom(stream); + } + + @VisibleForTesting + public void clearModificationReference(){ + if(this.modificationReference != null) { + this.modificationReference.clear(); + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java index 1349e1ece3..0ad701b72e 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java @@ -29,19 +29,15 @@ final class RandomPrefix { String encodeQName(final QName qname) { String prefix = prefixes.get(qname.getNamespace()); if (prefix == null) { - prefix = qname.getPrefix(); - if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) { - final ThreadLocalRandom random = ThreadLocalRandom.current(); - do { - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 4; i++) { - sb.append((char)('a' + random.nextInt(25))); - } - - prefix = sb.toString(); - } while (prefixes.containsValue(prefix)); - } - + final ThreadLocalRandom random = ThreadLocalRandom.current(); + do { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 4; i++) { + sb.append((char)('a' + random.nextInt(25))); + } + + prefix = sb.toString(); + } while (prefixes.containsValue(prefix)); prefixes.put(qname.getNamespace(), prefix); } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java index 0f93f43c56..c90d3e5d56 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java @@ -9,6 +9,12 @@ package org.opendaylight.controller.xml.codec; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; +import java.net.URI; +import java.util.Map.Entry; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.AttributesContainer; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -29,13 +35,6 @@ import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefi import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.net.URI; -import java.util.Map.Entry; - /** * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class * by no means final and subject to change as more functionality is centralized here. @@ -136,15 +135,14 @@ public class XmlStreamUtils { */ public void writeElement(final XMLStreamWriter writer, final @Nonnull Node data, final SchemaNode schema) throws XMLStreamException { final QName qname = data.getNodeType(); - final String pfx = qname.getPrefix() != null ? qname.getPrefix() : ""; final String ns = qname.getNamespace() != null ? qname.getNamespace().toString() : ""; if (isEmptyElement(data)) { - writer.writeEmptyElement(pfx, qname.getLocalName(), ns); + writer.writeEmptyElement("", qname.getLocalName(), ns); return; } - writer.writeStartElement(pfx, qname.getLocalName(), ns); + writer.writeStartElement("", qname.getLocalName(), ns); if (data instanceof AttributesContainer && ((AttributesContainer) data).getAttributes() != null) { for (Entry attribute : ((AttributesContainer) data).getAttributes().entrySet()) { writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue()); @@ -226,15 +224,9 @@ public class XmlStreamUtils { private static void write(final @Nonnull XMLStreamWriter writer, final @Nonnull IdentityrefTypeDefinition type, final @Nonnull Object value) throws XMLStreamException { if (value instanceof QName) { final QName qname = (QName) value; - final String prefix; - if (qname.getPrefix() != null && !qname.getPrefix().isEmpty()) { - prefix = qname.getPrefix(); - } else { - prefix = "x"; - } - writer.writeNamespace(prefix, qname.getNamespace().toString()); - writer.writeCharacters(prefix + ':' + qname.getLocalName()); + writer.writeNamespace("x", qname.getNamespace().toString()); + writer.writeCharacters("x:" + qname.getLocalName()); } else { if(LOG.isDebugEnabled()) { LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName().getLocalName(), value.getClass()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index a22e535fad..7d6dde9c8a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -69,6 +69,7 @@ import org.opendaylight.controller.cluster.notifications.RoleChangeNotifier; import org.opendaylight.controller.cluster.raft.RaftActor; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; @@ -321,7 +322,7 @@ public class Shard extends RaftActor { cohortEntry.getCohort().preCommit().get(); Shard.this.persistData(getSender(), transactionID, - new CompositeModificationPayload(cohortEntry.getModification().toSerializable())); + new CompositeModificationByteStringPayload(cohortEntry.getModification().toSerializable())); } catch (InterruptedException | ExecutionException e) { LOG.error(e, "An exception occurred while preCommitting transaction {}", cohortEntry.getTransactionID()); @@ -679,6 +680,8 @@ public class Shard extends RaftActor { protected void appendRecoveredLogEntry(final Payload data) { if (data instanceof CompositeModificationPayload) { currentLogRecoveryBatch.add(((CompositeModificationPayload) data).getModification()); + } else if (data instanceof CompositeModificationByteStringPayload) { + currentLogRecoveryBatch.add(((CompositeModificationByteStringPayload) data).getModification()); } else { LOG.error("Unknown state received {} during recovery", data); } @@ -755,19 +758,12 @@ public class Shard extends RaftActor { if (data instanceof CompositeModificationPayload) { Object modification = ((CompositeModificationPayload) data).getModification(); - if(modification == null) { - LOG.error( - "modification is null - this is very unexpected, clientActor = {}, identifier = {}", - identifier, clientActor != null ? clientActor.path().toString() : null); - } else if(clientActor == null) { - // There's no clientActor to which to send a commit reply so we must be applying - // replicated state from the leader. - commitWithNewTransaction(MutableCompositeModification.fromSerializable( - modification, schemaContext)); - } else { - // This must be the OK to commit after replication consensus. - finishCommit(clientActor, identifier); - } + applyModificationToState(clientActor, identifier, modification); + } else if(data instanceof CompositeModificationByteStringPayload ){ + Object modification = ((CompositeModificationByteStringPayload) data).getModification(); + + applyModificationToState(clientActor, identifier, modification); + } else { LOG.error("Unknown state received {} Class loader = {} CompositeNodeMod.ClassLoader = {}", data, data.getClass().getClassLoader(), @@ -778,6 +774,22 @@ public class Shard extends RaftActor { } + private void applyModificationToState(ActorRef clientActor, String identifier, Object modification) { + if(modification == null) { + LOG.error( + "modification is null - this is very unexpected, clientActor = {}, identifier = {}", + identifier, clientActor != null ? clientActor.path().toString() : null); + } else if(clientActor == null) { + // There's no clientActor to which to send a commit reply so we must be applying + // replicated state from the leader. + commitWithNewTransaction(MutableCompositeModification.fromSerializable( + modification, schemaContext)); + } else { + // This must be the OK to commit after replication consensus. + finishCommit(clientActor, identifier); + } + } + private void updateJournalStats() { ReplicatedLogEntry lastLogEntry = getLastLogEntry(); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationByteStringPayloadTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationByteStringPayloadTest.java new file mode 100644 index 0000000000..db9f3d1801 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationByteStringPayloadTest.java @@ -0,0 +1,83 @@ +/* + * 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.cluster.datastore; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.SerializationUtils; +import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.modification.Modification; +import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; +import org.opendaylight.controller.cluster.datastore.modification.WriteModification; +import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; +import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry; +import org.opendaylight.controller.cluster.raft.messages.AppendEntries; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload; +import org.opendaylight.controller.md.cluster.datastore.model.TestModel; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class CompositeModificationByteStringPayloadTest { + + private static final SchemaContext SCHEMA_CONTEXT = TestModel.createTestContext(); + + @Test + public void testSerialization(){ + WriteModification writeModification = + new WriteModification(TestModel.TEST_PATH, ImmutableNodes + .containerNode(TestModel.TEST_QNAME), + TestModel.createTestContext()); + + MutableCompositeModification compositeModification = + new MutableCompositeModification(); + + compositeModification.addModification(writeModification); + + CompositeModificationByteStringPayload compositeModificationByteStringPayload + = new CompositeModificationByteStringPayload(compositeModification.toSerializable()); + + byte[] bytes = SerializationUtils.serialize(compositeModificationByteStringPayload); + + Object deserialize = SerializationUtils.deserialize(bytes); + + assertTrue(deserialize instanceof CompositeModificationByteStringPayload); + + } + + @Test + public void testAppendEntries(){ + List entries = new ArrayList<>(); + + CompositeModificationByteStringPayload payload = newByteStringPayload( + new WriteModification(TestModel.OUTER_LIST_PATH, + ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(), + SCHEMA_CONTEXT)); + + payload.clearModificationReference(); + + entries.add(new ReplicatedLogImplEntry(0, 1, payload)); + + + assertNotNull(new AppendEntries(10, "foobar", 10, 10, entries, 10).toSerializable()); + } + + + + private CompositeModificationByteStringPayload newByteStringPayload(final Modification... mods) { + MutableCompositeModification compMod = new MutableCompositeModification(); + for(Modification mod: mods) { + compMod.addModification(mod); + } + + return new CompositeModificationByteStringPayload(compMod.toSerializable()); + } + +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java index 926cef6ba5..2792342ab2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java @@ -75,6 +75,7 @@ import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot; import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.client.messages.FindLeader; import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload; import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper; @@ -432,9 +433,9 @@ public class ShardTest extends AbstractActorTest { ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(), SCHEMA_CONTEXT)))); - int nListEntries = 11; + int nListEntries = 16; Set listEntryKeys = new HashSet<>(); - for(int i = 1; i <= nListEntries; i++) { + for(int i = 1; i <= nListEntries-5; i++) { listEntryKeys.add(Integer.valueOf(i)); YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH) .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build(); @@ -445,6 +446,19 @@ public class ShardTest extends AbstractActorTest { newPayload(mod))); } + // Add some of the new CompositeModificationByteStringPayload + for(int i = 11; i <= nListEntries; i++) { + listEntryKeys.add(Integer.valueOf(i)); + YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH) + .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build(); + Modification mod = new MergeModification(path, + ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i), + SCHEMA_CONTEXT); + InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1, + newByteStringPayload(mod))); + } + + InMemoryJournal.addEntry(shardID.toString(), nListEntries + 1, new ApplyLogEntries(nListEntries)); @@ -516,6 +530,16 @@ public class ShardTest extends AbstractActorTest { return new CompositeModificationPayload(compMod.toSerializable()); } + private CompositeModificationByteStringPayload newByteStringPayload(final Modification... mods) { + MutableCompositeModification compMod = new MutableCompositeModification(); + for(Modification mod: mods) { + compMod.addModification(mod); + } + + return new CompositeModificationByteStringPayload(compMod.toSerializable()); + } + + private DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName, final InMemoryDOMDataStore dataStore, final YangInstanceIdentifier path, final NormalizedNode data, final MutableCompositeModification modification) { diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotification.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotification.java new file mode 100644 index 0000000000..d99001ea5c --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotification.java @@ -0,0 +1,31 @@ +/* + * 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.api; + +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +/** + * A single YANG notification. + */ +public interface DOMNotification { + /** + * Return the type of this notification. + * + * @return Notification type. + */ + @Nonnull SchemaPath getType(); + + /** + * Return the body of this notification. + * + * @return Notification body. + */ + @Nonnull ContainerNode getBody(); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListener.java new file mode 100644 index 0000000000..7085588e87 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListener.java @@ -0,0 +1,24 @@ +/* + * 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.api; + +import java.util.EventListener; +import javax.annotation.Nonnull; + +/** + * Interface implemented by listeners interested in {@link DOMNotification}s. + */ +public interface DOMNotificationListener extends EventListener { + /** + * Invoked whenever a {@link DOMNotification} matching the subscription + * criteria is received. + * + * @param notification Received notification + */ + void onNotification(@Nonnull DOMNotification notification); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListenerRegistration.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListenerRegistration.java new file mode 100644 index 0000000000..4dccad2e42 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationListenerRegistration.java @@ -0,0 +1,18 @@ +/* + * 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.api; + +import org.opendaylight.yangtools.concepts.ListenerRegistration; + +/** + * A registration of a {@link DOMNotificationListener}. Invoking {@link #close()} will prevent further + * delivery of events to the listener. + */ +public interface DOMNotificationListenerRegistration extends ListenerRegistration { + +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationPublishService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationPublishService.java new file mode 100644 index 0000000000..8a845e8729 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationPublishService.java @@ -0,0 +1,112 @@ +/* + * 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.api; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; + +/** + * A {@link DOMService} which allows its user to send {@link DOMNotification}s. It + * provides two styles of initiating the notification delivery, similar to + * {@link java.util.concurrent.BlockingQueue}: + * - a put-style method which waits until the implementation can accept the notification + * for delivery, and + * - an offer-style method, which attempts to enqueue the notification, but allows + * the caller to specify that it should never wait, or put an upper bound on how + * long it is going to wait. + */ +public interface DOMNotificationPublishService extends DOMService { + /** + * Well-known value indicating that the implementation is currently not + * able to accept a notification. + */ + ListenableFuture REJECTED = Futures.immediateFailedFuture(new Throwable("Unacceptable blocking conditions encountered")); + + /** + * Publish a notification. The result of this method is a {@link ListenableFuture} + * which will complete once the notification has been delivered to all immediate + * registrants. The type of the object resulting from the future is not defined + * and implementations may use it to convey additional information related to the + * publishing process. + * + * Abstract subclasses can refine the return type as returning a promise of a + * more specific type, e.g.: + * + * public interface DeliveryStatus { int getListenerCount(); } + * ListenableFuture putNotification(DOMNotification notification); + * + * Once the Future succeeds, the resulting object can be queried for traits using + * instanceof, e.g: + * + * // Can block when (for example) the implemention's ThreadPool queue is full + * Object o = service.putNotification(notif).get(); + * if (o instanceof DeliveryStatus) { + * DeliveryStatus ds = (DeliveryStatus)o; + * LOG.debug("Notification was received by {} listeners", ds.getListenerCount();); + * } + * } + * + * In case an implementation is running out of resources, it can block the calling + * thread until enough resources become available to accept the notification for + * processing, or it is interrupted. + * + * Caution: completion here means that the implementation has completed processing + * of the notification. This does not mean that all existing registrants + * have seen the notification. Most importantly, the delivery process at + * other cluster nodes may have not begun yet. + * + * @param notification Notification to be published. + * @return A listenable future which will report completion when the service + * has finished propagating the notification to its immediate registrants. + * @throws InterruptedException if interrupted while waiting + * @throws NullPointerException if notification is null. + */ + @Nonnull ListenableFuture putNotification(@Nonnull DOMNotification notification) throws InterruptedException; + + /** + * Attempt to publish a notification. The result of this method is a {@link ListenableFuture} + * which will complete once the notification has been delivered to all immediate + * registrants. The type of the object resulting from the future is not defined + * and implementations may use it to convey additional information related to the + * publishing process. Unlike {@link #putNotification(DOMNotification)}, this method + * is guaranteed not to block if the underlying implementation encounters contention. + * + * @param notification Notification to be published. + * @return A listenable future which will report completion when the service + * has finished propagating the notification to its immediate registrants, + * or {@value #REJECTED} if resource constraints prevent + * the implementation from accepting the notification for delivery. + * @throws NullPointerException if notification is null. + */ + @Nonnull ListenableFuture offerNotification(@Nonnull DOMNotification notification); + + /** + * Attempt to publish a notification. The result of this method is a {@link ListenableFuture} + * which will complete once the notification has been delivered to all immediate + * registrants. The type of the object resulting from the future is not defined + * and implementations may use it to convey additional information related to the + * publishing process. Unlike {@link #putNotification(DOMNotification)}, this method + * is guaranteed to block more than the specified timeout. + * + * @param notification Notification to be published. + * @param timeout how long to wait before giving up, in units of unit + * @param unit a TimeUnit determining how to interpret the timeout parameter + * @return A listenable future which will report completion when the service + * has finished propagating the notification to its immediate registrants, + * or {@value #REJECTED} if resource constraints prevent + * the implementation from accepting the notification for delivery. + * @throws InterruptedException if interrupted while waiting + * @throws NullPointerException if notification or unit is null. + * @throws IllegalArgumentException if timeout is negative. + */ + @Nonnull ListenableFuture offerNotification(@Nonnull DOMNotification notification, + @Nonnegative long timeout, @Nonnull TimeUnit unit) throws InterruptedException; +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationService.java new file mode 100644 index 0000000000..6bce9c447a --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMNotificationService.java @@ -0,0 +1,51 @@ +/* + * 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.api; + +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +/** + * A {@link DOMService} which allows its users to subscribe to receive + * {@link DOMNotification}s. + */ +public interface DOMNotificationService { + /** + * Register a {@link DOMNotificationListener} to receive a set of notifications. As with + * other ListenerRegistration-based interfaces, registering an instance multiple times + * results in notifications being delivered for each registration. + * + * @param listener Notification instance to register + * @param types Notification types which should be delivered to the listener. Duplicate + * entries are processed only once, null entries are ignored. + * @return Registration handle. Invoking {@link DOMNotificationListenerRegistration#close()} + * will stop the delivery of notifications to the listener + * @throws IllegalArgumentException if types is empty or contains an invalid element, such as + * null or a SchemaPath which does not represent a valid {@link DOMNotification} type. + * @throws NullPointerException if either of the arguments is null + */ + DOMNotificationListenerRegistration registerNotificationListener(@Nonnull DOMNotificationListener listener, @Nonnull Collection types); + + /** + * Register a {@link DOMNotificationListener} to receive a set of notifications. As with + * other ListenerRegistration-based interfaces, registering an instance multiple times + * results in notifications being delivered for each registration. + * + * @param listener Notification instance to register + * @param types Notification types which should be delivered to the listener. Duplicate + * entries are processed only once, null entries are ignored. + * @return Registration handle. Invoking {@link DOMNotificationListenerRegistration#close()} + * will stop the delivery of notifications to the listener + * @throws IllegalArgumentException if types is empty or contains an invalid element, such as + * null or a SchemaPath which does not represent a valid {@link DOMNotification} type. + * @throws NullPointerException if listener is null + */ + // FIXME: Java 8: provide a default implementation of this method. + DOMNotificationListenerRegistration registerNotificationListener(@Nonnull DOMNotificationListener listener, SchemaPath... types); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java index 357cb8bfe6..dc18394ffd 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMService.java @@ -5,9 +5,12 @@ * 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.api; +/** + * Marker interface for services which can be obtained from a {@link DOMMountPoint} + * instance. No further semantics are implied. + */ public interface DOMService { } diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMNotificationListenerRegistration.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMNotificationListenerRegistration.java new file mode 100644 index 0000000000..2934b0da35 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/AbstractDOMNotificationListenerRegistration.java @@ -0,0 +1,30 @@ +/* + * 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.spi; + +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListenerRegistration; +import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; + +/** + * Utility base class for {@link DOMNotificationListenerRegistration} + * implementations. + */ +public abstract class AbstractDOMNotificationListenerRegistration extends AbstractListenerRegistration implements DOMNotificationListenerRegistration { + /** + * Default constructor. Subclasses need to invoke it from their + * constructor(s). + * + * @param listener {@link DOMNotificationListener} instance which is + * being held by this registration. May not be null. + */ + protected AbstractDOMNotificationListenerRegistration(final @Nonnull DOMNotificationListener listener) { + super(listener); + } +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationPublishService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationPublishService.java new file mode 100644 index 0000000000..6bc11079e2 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationPublishService.java @@ -0,0 +1,39 @@ +/* + * 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.spi; + +import com.google.common.collect.ForwardingObject; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService; + +/** + * Utility implementations of {@link DOMNotificationPublishService} which forwards + * all requests to a delegate instance. + */ +public abstract class ForwardingDOMNotificationPublishService extends ForwardingObject implements DOMNotificationPublishService { + @Override + protected abstract DOMNotificationPublishService delegate(); + + @Override + public ListenableFuture putNotification(final DOMNotification notification) throws InterruptedException { + return delegate().putNotification(notification); + } + + @Override + public ListenableFuture offerNotification(final DOMNotification notification) { + return delegate().offerNotification(notification); + } + + @Override + public ListenableFuture offerNotification(final DOMNotification notification, final long timeout, + final TimeUnit unit) throws InterruptedException { + return delegate().offerNotification(notification, timeout, unit); + } +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationService.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationService.java new file mode 100644 index 0000000000..5199a3893b --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/spi/ForwardingDOMNotificationService.java @@ -0,0 +1,36 @@ +/* + * 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.spi; + +import com.google.common.collect.ForwardingObject; +import java.util.Collection; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListenerRegistration; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +/** + * Utility implementation of a {@link DOMNotificationService} which forwards all requests + * to a delegate instance. + */ +public abstract class ForwardingDOMNotificationService extends ForwardingObject implements DOMNotificationService { + @Override + protected abstract DOMNotificationService delegate(); + + @Override + public DOMNotificationListenerRegistration registerNotificationListener(final DOMNotificationListener listener, + final Collection types) { + return delegate().registerNotificationListener(listener, types); + } + + @Override + public DOMNotificationListenerRegistration registerNotificationListener(final DOMNotificationListener listener, + final SchemaPath... types) { + return delegate().registerNotificationListener(listener, types); + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestUtil.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestUtil.java index 7890931a27..b5a2b42a64 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestUtil.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestUtil.java @@ -34,25 +34,25 @@ public final class RestUtil { } public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) { - String valueTrimmed = value.trim(); + final String valueTrimmed = value.trim(); if (!valueTrimmed.startsWith("/")) { return null; } - String[] xPathParts = valueTrimmed.split("/"); + final String[] xPathParts = valueTrimmed.split("/"); if (xPathParts.length < 2) { // must be at least "/pr:node" return null; } - IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value); + final IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value); for (int i = 1; i < xPathParts.length; i++) { - String xPathPartTrimmed = xPathParts[i].trim(); + final String xPathPartTrimmed = xPathParts[i].trim(); - String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed); - IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap); + final String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed); + final IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap); if (identityValue == null) { return null; } - List predicates = toPredicates(xPathPartTrimmed, prefixMap); + final List predicates = toPredicates(xPathPartTrimmed, prefixMap); if (predicates == null) { return null; } @@ -64,47 +64,47 @@ public final class RestUtil { } private static String getIdAndPrefixAsStr(final String pathPart) { - int predicateStartIndex = pathPart.indexOf("["); + final int predicateStartIndex = pathPart.indexOf("["); return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex); } private static IdentityValue toIdentity(final String xPathPart, final PrefixesMaping prefixMap) { - String xPathPartTrimmed = xPathPart.trim(); + final String xPathPartTrimmed = xPathPart.trim(); if (xPathPartTrimmed.isEmpty()) { return null; } - String[] prefixAndIdentifier = xPathPartTrimmed.split(":"); + final String[] prefixAndIdentifier = xPathPartTrimmed.split(":"); // it is not "prefix:value" if (prefixAndIdentifier.length != 2) { return null; } - String prefix = prefixAndIdentifier[0].trim(); - String identifier = prefixAndIdentifier[1].trim(); + final String prefix = prefixAndIdentifier[0].trim(); + final String identifier = prefixAndIdentifier[1].trim(); if (prefix.isEmpty() || identifier.isEmpty()) { return null; } - String namespace = prefixMap.getNamespace(prefix); - return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix); + final String namespace = prefixMap.getNamespace(prefix); + return new IdentityValue(namespace, identifier); } private static List toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) { - List result = new ArrayList<>(); - List predicates = new ArrayList<>(); - Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr); + final List result = new ArrayList<>(); + final List predicates = new ArrayList<>(); + final Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr); while (matcher.find()) { predicates.add(matcher.group(1).trim()); } - for (String predicate : predicates) { - int indexOfEqualityMark = predicate.indexOf("="); + for (final String predicate : predicates) { + final int indexOfEqualityMark = predicate.indexOf("="); if (indexOfEqualityMark != -1) { - String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1)); + final String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1)); if (predicate.startsWith(".")) { // it is leaf-list if (predicateValue == null) { return null; } result.add(new Predicate(null, predicateValue)); } else { - IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap); + final IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap); if (identityValue == null || predicateValue == null) { return null; } @@ -116,7 +116,7 @@ public final class RestUtil { } private static String toPredicateValue(final String predicatedValue) { - String predicatedValueTrimmed = predicatedValue.trim(); + final String predicatedValueTrimmed = predicatedValue.trim(); if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE)) && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) { return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1); diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/IdentityValuesDTO.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/IdentityValuesDTO.java index 4e797d9057..248830830e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/IdentityValuesDTO.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/IdentityValuesDTO.java @@ -16,12 +16,12 @@ public final class IdentityValuesDTO { private final List elementData = new ArrayList<>(); private final String originValue; - public IdentityValuesDTO(String namespace, String value, String prefix, String originValue) { - elementData.add(new IdentityValue(namespace, value, prefix)); + public IdentityValuesDTO(final String namespace, final String value, final String prefix, final String originValue) { + elementData.add(new IdentityValue(namespace, value)); this.originValue = originValue; } - public IdentityValuesDTO(String originValue) { + public IdentityValuesDTO(final String originValue) { this.originValue = originValue; } @@ -29,11 +29,11 @@ public final class IdentityValuesDTO { originValue = null; } - public void add(String namespace, String value, String prefix) { - elementData.add(new IdentityValue(namespace, value, prefix)); + public void add(final String namespace, final String value, final String prefix) { + elementData.add(new IdentityValue(namespace, value)); } - public void add(IdentityValue identityValue) { + public void add(final IdentityValue identityValue) { elementData.add(identityValue); } @@ -54,13 +54,11 @@ public final class IdentityValuesDTO { private final String namespace; private final String value; - private final String prefix; private List predicates; - public IdentityValue(String namespace, String value, String prefix) { + public IdentityValue(final String namespace, final String value) { this.namespace = namespace; this.value = value; - this.prefix = prefix; } public String getNamespace() { @@ -71,9 +69,6 @@ public final class IdentityValuesDTO { return value; } - public String getPrefix() { - return prefix; - } public List getPredicates() { if (predicates == null) { @@ -82,24 +77,21 @@ public final class IdentityValuesDTO { return Collections.unmodifiableList(predicates); } - public void setPredicates(List predicates) { + public void setPredicates(final List predicates) { this.predicates = predicates; } @Override public String toString() { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); if (namespace != null) { sb.append(namespace); } - if (prefix != null) { - sb.append("(").append(prefix).append(")"); - } if (value != null) { sb.append(" - ").append(value); } if (predicates != null && !predicates.isEmpty()) { - for (Predicate predicate : predicates) { + for (final Predicate predicate : predicates) { sb.append("["); predicate.toString(); sb.append("]"); @@ -115,7 +107,7 @@ public final class IdentityValuesDTO { private final IdentityValue name; private final String value; - public Predicate(IdentityValue name, String value) { + public Predicate(final IdentityValue name, final String value) { super(); this.name = name; this.value = value; @@ -131,7 +123,7 @@ public final class IdentityValuesDTO { @Override public String toString() { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); if (name != null) { sb.append(name.toString()); } 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 ceac03e3d9..763d1c604e 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 @@ -97,7 +97,7 @@ public class RestCodec { input == null ? "null" : input.getClass(), String.valueOf(input)); return null; } else { - TypeDefinitionAwareCodec> typeAwarecodec = TypeDefinitionAwareCodec + final TypeDefinitionAwareCodec> typeAwarecodec = TypeDefinitionAwareCodec .from(type); if (typeAwarecodec != null) { if (input instanceof IdentityValuesDTO) { @@ -110,7 +110,7 @@ public class RestCodec { return null; } } - } catch (ClassCastException e) { // TODO remove this catch when everyone use codecs + } catch (final ClassCastException e) { // TODO remove this catch when everyone use codecs logger.error( "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input), e); @@ -129,7 +129,7 @@ public class RestCodec { } else if (type instanceof InstanceIdentifierTypeDefinition) { return instanceIdentifier.serialize(input); } else { - TypeDefinitionAwareCodec> typeAwarecodec = TypeDefinitionAwareCodec + final TypeDefinitionAwareCodec> typeAwarecodec = TypeDefinitionAwareCodec .from(type); if (typeAwarecodec != null) { return typeAwarecodec.serialize(input); @@ -139,7 +139,7 @@ public class RestCodec { return null; } } - } catch (ClassCastException e) { // TODO remove this catch when everyone use codecs + } catch (final ClassCastException e) { // TODO remove this catch when everyone use codecs logger.error( "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input), e); @@ -161,13 +161,13 @@ public class RestCodec { @Override public IdentityValuesDTO serialize(final QName data) { - return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), data.getPrefix(), null); + return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), null, null); } @Override public QName deserialize(final IdentityValuesDTO data) { - IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); - Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint); + final IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); + final Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint); if (module == null) { logger.info("Module was not found for namespace {}", valueWithNamespace.getNamespace()); logger.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace)); @@ -203,16 +203,16 @@ public class RestCodec { @Override public IdentityValuesDTO serialize(final YangInstanceIdentifier data) { - IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(); - for (PathArgument pathArgument : data.getPathArguments()) { - IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType()); + final IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(); + for (final PathArgument pathArgument : data.getPathArguments()) { + final IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType()); if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) { - List predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument) + final List predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument) .getKeyValues()); identityValue.setPredicates(predicates); } else if (pathArgument instanceof NodeWithValue && identityValue != null) { - List predicates = new ArrayList<>(); - String value = String.valueOf(((NodeWithValue) pathArgument).getValue()); + final List predicates = new ArrayList<>(); + final String value = String.valueOf(((NodeWithValue) pathArgument).getValue()); predicates.add(new Predicate(null, value)); identityValue.setPredicates(predicates); } @@ -223,9 +223,9 @@ public class RestCodec { @Override public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) { - List result = new ArrayList(); - IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); - Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint); + final List result = new ArrayList(); + final IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); + final Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint); if (module == null) { logger.info("Module by namespace '{}' of first node in instance-identifier was not found.", valueWithNamespace.getNamespace()); @@ -235,11 +235,11 @@ public class RestCodec { } DataNodeContainer parentContainer = module; - List identities = data.getValuesWithNamespaces(); + final List identities = data.getValuesWithNamespaces(); for (int i = 0; i < identities.size(); i++) { - IdentityValue identityValue = identities.get(i); + final IdentityValue identityValue = identities.get(i); URI validNamespace = resolveValidNamespace(identityValue.getNamespace(), mountPoint); - DataSchemaNode node = ControllerContext.findInstanceDataChildByNameAndNamespace( + final DataSchemaNode node = ControllerContext.findInstanceDataChildByNameAndNamespace( parentContainer, identityValue.getValue(), validNamespace); if (node == null) { logger.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes()); @@ -247,13 +247,13 @@ public class RestCodec { String.valueOf(identityValue.getValue())); return null; } - QName qName = node.getQName(); + final QName qName = node.getQName(); PathArgument pathArgument = null; if (identityValue.getPredicates().isEmpty()) { pathArgument = new NodeIdentifier(qName); } else { if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry - Predicate leafListPredicate = identityValue.getPredicates().get(0); + final Predicate leafListPredicate = identityValue.getPredicates().get(0); if (!leafListPredicate.isLeafList()) { logger.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\""); logger.info("Instance-identifier will be translated as NULL for data - {}", @@ -262,11 +262,11 @@ public class RestCodec { } pathArgument = new NodeWithValue(qName, leafListPredicate.getValue()); } else if (node instanceof ListSchemaNode) { // predicates are keys of list - DataNodeContainer listNode = (DataNodeContainer) node; - Map predicatesMap = new HashMap<>(); - for (Predicate predicate : identityValue.getPredicates()) { + final DataNodeContainer listNode = (DataNodeContainer) node; + final Map predicatesMap = new HashMap<>(); + for (final Predicate predicate : identityValue.getPredicates()) { validNamespace = resolveValidNamespace(predicate.getName().getNamespace(), mountPoint); - DataSchemaNode listKey = ControllerContext + final DataSchemaNode listKey = ControllerContext .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(), validNamespace); predicatesMap.put(listKey.getQName(), predicate.getValue()); @@ -297,9 +297,9 @@ public class RestCodec { } private List keyValuesToPredicateList(final Map keyValues) { - List result = new ArrayList<>(); - for (QName qName : keyValues.keySet()) { - Object value = keyValues.get(qName); + final List result = new ArrayList<>(); + for (final QName qName : keyValues.keySet()) { + final Object value = keyValues.get(qName); result.add(new Predicate(qNameToIdentityValue(qName), String.valueOf(value))); } return result; @@ -307,14 +307,14 @@ public class RestCodec { private IdentityValue qNameToIdentityValue(final QName qName) { if (qName != null) { - return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName(), qName.getPrefix()); + return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName()); } return null; } } private static Module getModuleByNamespace(final String namespace, final DOMMountPoint mountPoint) { - URI validNamespace = resolveValidNamespace(namespace, mountPoint); + final URI validNamespace = resolveValidNamespace(namespace, mountPoint); Module module = null; if (mountPoint != null) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/ListenerAdapter.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/ListenerAdapter.java index a6e02632ce..8481a9f0c8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/ListenerAdapter.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/ListenerAdapter.java @@ -96,13 +96,13 @@ public class ListenerAdapter implements DOMDataChangeListener { } @Override - public void onDataChanged(AsyncDataChangeEvent> change) { + public void onDataChanged(final AsyncDataChangeEvent> change) { // TODO Auto-generated method stub if (!change.getCreatedData().isEmpty() || !change.getUpdatedData().isEmpty() || !change.getRemovedPaths().isEmpty()) { - String xml = prepareXmlFrom(change); - Event event = new Event(EventType.NOTIFY); + final String xml = prepareXmlFrom(change); + final Event event = new Event(EventType.NOTIFY); event.setData(xml); eventBus.post(event); } @@ -115,7 +115,7 @@ public class ListenerAdapter implements DOMDataChangeListener { @Subscribe public void recordCustomerChange(final Event event) { if (event.getType() == EventType.REGISTER) { - Channel subscriber = event.getSubscriber(); + final Channel subscriber = event.getSubscriber(); if (!subscribers.contains(subscriber)) { subscribers.add(subscriber); } @@ -123,7 +123,7 @@ public class ListenerAdapter implements DOMDataChangeListener { subscribers.remove(event.getSubscriber()); Notificator.removeListenerIfNoSubscriberExists(ListenerAdapter.this); } else if (event.getType() == EventType.NOTIFY) { - for (Channel subscriber : subscribers) { + for (final Channel subscriber : subscribers) { if (subscriber.isActive()) { LOG.debug("Data are sent to subscriber {}:", subscriber.remoteAddress()); subscriber.writeAndFlush(new TextWebSocketFrame(event.getData())); @@ -218,34 +218,34 @@ public class ListenerAdapter implements DOMDataChangeListener { * DataChangeEvent * @return Data in printable form. */ - private String prepareXmlFrom(AsyncDataChangeEvent> change) { - Document doc = createDocument(); - Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0", + private String prepareXmlFrom(final AsyncDataChangeEvent> change) { + final Document doc = createDocument(); + final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0", "notification"); doc.appendChild(notificationElement); - Element eventTimeElement = doc.createElement("eventTime"); + final Element eventTimeElement = doc.createElement("eventTime"); eventTimeElement.setTextContent(toRFC3339(new Date())); notificationElement.appendChild(eventTimeElement); - Element dataChangedNotificationEventElement = doc.createElementNS( + final Element dataChangedNotificationEventElement = doc.createElementNS( "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification"); addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change); notificationElement.appendChild(dataChangedNotificationEventElement); try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Transformer transformer = FACTORY.newTransformer(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final Transformer transformer = FACTORY.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, Charsets.UTF_8))); - byte[] charData = out.toByteArray(); + final byte[] charData = out.toByteArray(); return new String(charData, "UTF-8"); } catch (TransformerException | UnsupportedEncodingException e) { - String msg = "Error during transformation of Document into String"; + final String msg = "Error during transformation of Document into String"; LOG.error(msg, e); return msg; } @@ -270,7 +270,7 @@ public class ListenerAdapter implements DOMDataChangeListener { final DocumentBuilder bob; try { bob = DBF.newDocumentBuilder(); - } catch (ParserConfigurationException e) { + } catch (final ParserConfigurationException e) { return null; } return bob.newDocument(); @@ -288,7 +288,7 @@ public class ListenerAdapter implements DOMDataChangeListener { */ private void addValuesToDataChangedNotificationEventElement(final Document doc, final Element dataChangedNotificationEventElement, - AsyncDataChangeEvent> change) { + final AsyncDataChangeEvent> change) { addValuesFromDataToElement(doc, change.getCreatedData().keySet(), dataChangedNotificationEventElement, Operation.CREATED); if (change.getCreatedData().isEmpty()) { @@ -313,13 +313,13 @@ public class ListenerAdapter implements DOMDataChangeListener { * @param operation * {@link Operation} */ - private void addValuesFromDataToElement(Document doc, Set data, Element element, - Operation operation) { + private void addValuesFromDataToElement(final Document doc, final Set data, final Element element, + final Operation operation) { if (data == null || data.isEmpty()) { return; } - for (YangInstanceIdentifier path : data) { - Node node = createDataChangeEventElement(doc, path, null, operation); + for (final YangInstanceIdentifier path : data) { + final Node node = createDataChangeEventElement(doc, path, null, operation); element.appendChild(node); } } @@ -338,13 +338,13 @@ public class ListenerAdapter implements DOMDataChangeListener { * @param operation * {@link Operation} */ - private void addValuesFromDataToElement(Document doc, Map data, Element element, - Operation operation) { + private void addValuesFromDataToElement(final Document doc, final Map data, final Element element, + final Operation operation) { if (data == null || data.isEmpty()) { return; } - for (Entry entry : data.entrySet()) { - Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), operation); + for (final Entry entry : data.entrySet()) { + final Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), operation); element.appendChild(node); } } @@ -364,11 +364,10 @@ public class ListenerAdapter implements DOMDataChangeListener { * {@link Operation} * @return {@link Node} node represented by changed event element. */ - private Node createDataChangeEventElement(Document doc, YangInstanceIdentifier path, CompositeNode data, - Operation operation) { - Element dataChangeEventElement = doc.createElement("data-change-event"); - - Element pathElement = doc.createElement("path"); + private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path, final CompositeNode data, + final Operation operation) { + final Element dataChangeEventElement = doc.createElement("data-change-event"); + final Element pathElement = doc.createElement("path"); addPathAsValueToElement(path, pathElement); dataChangeEventElement.appendChild(pathElement); @@ -376,14 +375,14 @@ public class ListenerAdapter implements DOMDataChangeListener { // storeElement.setTextContent(store.value); // dataChangeEventElement.appendChild(storeElement); - Element operationElement = doc.createElement("operation"); + final Element operationElement = doc.createElement("operation"); operationElement.setTextContent(operation.value); dataChangeEventElement.appendChild(operationElement); if (data != null) { - Element dataElement = doc.createElement("data"); - Node dataAnyXml = translateToXml(path, data); - Node adoptedNode = doc.adoptNode(dataAnyXml); + final Element dataElement = doc.createElement("data"); + final Node dataAnyXml = translateToXml(path, data); + final Node adoptedNode = doc.adoptNode(dataAnyXml); dataElement.appendChild(adoptedNode); dataChangeEventElement.appendChild(dataElement); } @@ -401,7 +400,7 @@ public class ListenerAdapter implements DOMDataChangeListener { * @return Data in XML format. */ private Node translateToXml(final YangInstanceIdentifier path, final CompositeNode data) { - DataNodeContainer schemaNode = ControllerContext.getInstance().getDataNodeContainerFor(path); + final DataNodeContainer schemaNode = ControllerContext.getInstance().getDataNodeContainerFor(path); if (schemaNode == null) { LOG.info( "Path '{}' contains node with unsupported type (supported type is Container or List) or some node was not found.", @@ -409,9 +408,9 @@ public class ListenerAdapter implements DOMDataChangeListener { return null; } try { - Document xml = xmlMapper.write(data, schemaNode); + final Document xml = xmlMapper.write(data, schemaNode); return xml.getFirstChild(); - } catch (UnsupportedDataTypeException e) { + } catch (final UnsupportedDataTypeException e) { LOG.error("Error occured during translation of notification to XML.", e); return null; } @@ -427,18 +426,18 @@ public class ListenerAdapter implements DOMDataChangeListener { */ private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element) { // Map< key = namespace, value = prefix> - Map prefixes = new HashMap<>(); - YangInstanceIdentifier instanceIdentifier = path; - StringBuilder textContent = new StringBuilder(); + final Map prefixes = new HashMap<>(); + final YangInstanceIdentifier instanceIdentifier = path; + final StringBuilder textContent = new StringBuilder(); // FIXME: BUG-1281: this is duplicated code from yangtools (BUG-1275) - for (PathArgument pathArgument : instanceIdentifier.getPathArguments()) { + for (final PathArgument pathArgument : instanceIdentifier.getPathArguments()) { textContent.append("/"); writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes); if (pathArgument instanceof NodeIdentifierWithPredicates) { - Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); - for (QName keyValue : predicates.keySet()) { - String predicateValue = String.valueOf(predicates.get(keyValue)); + final Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); + for (final QName keyValue : predicates.keySet()) { + final String predicateValue = String.valueOf(predicates.get(keyValue)); textContent.append("["); writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes); textContent.append("='"); @@ -470,13 +469,10 @@ public class ListenerAdapter implements DOMDataChangeListener { */ private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, final QName qName, final Map prefixes) { - String namespace = qName.getNamespace().toString(); + final String namespace = qName.getNamespace().toString(); String prefix = prefixes.get(namespace); if (prefix == null) { - prefix = qName.getPrefix(); - if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) { - prefix = generateNewPrefix(prefixes.values()); - } + prefix = generateNewPrefix(prefixes.values()); } element.setAttribute("xmlns:" + prefix, namespace); @@ -496,11 +492,11 @@ public class ListenerAdapter implements DOMDataChangeListener { */ private static String generateNewPrefix(final Collection prefixes) { StringBuilder result = null; - Random random = new Random(); + final Random random = new Random(); do { result = new StringBuilder(); for (int i = 0; i < 4; i++) { - int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26); + final int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26); result.append(Character.toChars(randomNumber)); } } while (prefixes.contains(result.toString())); @@ -566,7 +562,7 @@ public class ListenerAdapter implements DOMDataChangeListener { if (!subscriber.isActive()) { LOG.debug("Channel is not active between websocket server and subscriber {}" + subscriber.remoteAddress()); } - Event event = new Event(EventType.REGISTER); + final Event event = new Event(EventType.REGISTER); event.setSubscriber(subscriber); eventBus.post(event); } @@ -579,7 +575,7 @@ public class ListenerAdapter implements DOMDataChangeListener { */ public void removeSubscriber(final Channel subscriber) { LOG.debug("Subscriber {} is removed.", subscriber.remoteAddress()); - Event event = new Event(EventType.DEREGISTER); + final Event event = new Event(EventType.DEREGISTER); event.setSubscriber(subscriber); eventBus.post(event); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java index 3d5fee3ab8..4232c83d5b 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/xml/test/CnSnToXmlTest.java @@ -70,7 +70,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangIdentityrefWithQNamePrefixToXMLTest() { serializeToXml(prepareIdentityrefData("prefix", true), - "prefix:iden"); + "",":iden"); } @Test @@ -93,7 +93,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangInt8ToXmlTest() { - String elName = "lfInt8"; + final String elName = "lfInt8"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int8.getInstance()).deserialize("14"), elName), "<" + elName + ">14"); @@ -101,7 +101,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangInt16ToXmlTest() { - String elName = "lfInt16"; + final String elName = "lfInt16"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int16.getInstance()).deserialize("3000"), elName), "<" + elName + ">3000"); @@ -109,7 +109,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangInt32ToXmlTest() { - String elName = "lfInt32"; + final String elName = "lfInt32"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int32.getInstance()).deserialize("201234"), elName), "<" + elName + ">201234"); @@ -117,7 +117,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangInt64ToXmlTest() { - String elName = "lfInt64"; + final String elName = "lfInt64"; serializeToXml( prepareCnStructForYangData( TypeDefinitionAwareCodec.from(Int64.getInstance()).deserialize("5123456789"), elName), "<" @@ -126,7 +126,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangUint8ToXmlTest() { - String elName = "lfUint8"; + final String elName = "lfUint8"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint8.getInstance()).deserialize("200"), elName), "<" + elName + ">200"); @@ -134,7 +134,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangUint16ToXmlTest() { - String elName = "lfUint16"; + final String elName = "lfUint16"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint16.getInstance()).deserialize("4000"), elName), "<" + elName + ">4000"); @@ -142,7 +142,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangUint32ToXmlTest() { - String elName = "lfUint32"; + final String elName = "lfUint32"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint32.getInstance()) .deserialize("4123456789"), elName), "<" + elName + ">4123456789"); @@ -150,7 +150,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangUint64ToXmlTest() { - String elName = "lfUint64"; + final String elName = "lfUint64"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint64.getInstance()) .deserialize("5123456789"), elName), "<" + elName + ">5123456789"); @@ -158,7 +158,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangBinaryToXmlTest() { - String elName = "lfBinary"; + final String elName = "lfBinary"; serializeToXml( prepareCnStructForYangData( TypeDefinitionAwareCodec.from(BinaryType.getInstance()).deserialize( @@ -168,13 +168,13 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangBitsToXmlTest() { - BitsTypeDefinition.Bit mockBit1 = mock(BitsTypeDefinition.Bit.class); + final BitsTypeDefinition.Bit mockBit1 = mock(BitsTypeDefinition.Bit.class); when(mockBit1.getName()).thenReturn("one"); - BitsTypeDefinition.Bit mockBit2 = mock(BitsTypeDefinition.Bit.class); + final BitsTypeDefinition.Bit mockBit2 = mock(BitsTypeDefinition.Bit.class); when(mockBit2.getName()).thenReturn("two"); - List bitList = Lists.newArrayList(mockBit1, mockBit2); + final List bitList = Lists.newArrayList(mockBit1, mockBit2); - String elName = "lfBits"; + final String elName = "lfBits"; serializeToXml( prepareCnStructForYangData( TypeDefinitionAwareCodec.from(BitsType.create(mock(SchemaPath.class), bitList)).deserialize( @@ -184,11 +184,11 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangEnumerationToXmlTest() { - EnumTypeDefinition.EnumPair mockEnum = mock(EnumTypeDefinition.EnumPair.class); + final EnumTypeDefinition.EnumPair mockEnum = mock(EnumTypeDefinition.EnumPair.class); when(mockEnum.getName()).thenReturn("enum2"); - List enumList = Lists.newArrayList(mockEnum); + final List enumList = Lists.newArrayList(mockEnum); - String elName = "lfEnumeration"; + final String elName = "lfEnumeration"; serializeToXml( prepareCnStructForYangData( TypeDefinitionAwareCodec.from( @@ -199,7 +199,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangEmptyToXmlTest() { - String elName = "lfEmpty"; + final String elName = "lfEmpty"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(EmptyType.getInstance()).deserialize(null), elName), "<" + elName + "/>"); @@ -207,7 +207,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangBooleanToXmlTest() { - String elName = "lfBoolean"; + final String elName = "lfBoolean"; serializeToXml( prepareCnStructForYangData(TypeDefinitionAwareCodec.from(BooleanType.getInstance()).deserialize("str"), elName), "<" + elName + ">false"); @@ -220,26 +220,26 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { @Test public void snAsYangUnionToXmlTest() { - BitsTypeDefinition.Bit mockBit1 = mock(BitsTypeDefinition.Bit.class); + final BitsTypeDefinition.Bit mockBit1 = mock(BitsTypeDefinition.Bit.class); when(mockBit1.getName()).thenReturn("first"); - BitsTypeDefinition.Bit mockBit2 = mock(BitsTypeDefinition.Bit.class); + final BitsTypeDefinition.Bit mockBit2 = mock(BitsTypeDefinition.Bit.class); when(mockBit2.getName()).thenReturn("second"); - List bitList = Lists.newArrayList(mockBit1, mockBit2); + final List bitList = Lists.newArrayList(mockBit1, mockBit2); - List> types = Lists.> newArrayList(Int8.getInstance(), + final List> types = Lists.> newArrayList(Int8.getInstance(), BitsType.create(mock(SchemaPath.class), bitList), BooleanType.getInstance()); - UnionType unionType = UnionType.create(types); + final UnionType unionType = UnionType.create(types); - String elName = "lfUnion"; - String int8 = "15"; + final String elName = "lfUnion"; + final String int8 = "15"; serializeToXml(prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(int8), elName), "<" + elName + ">15"); - String bits = "first second"; + final String bits = "first second"; serializeToXml(prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bits), elName), "<" + elName + ">first second"); - String bool = "str"; + final String bool = "str"; serializeToXml(prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bool), elName), "<" + elName + ">str"); } @@ -255,7 +255,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { assertNotNull(xmlString); boolean containSearchedStr = false; String strRepresentation = ""; - for (String searchedStr : xmlRepresentation) { + for (final String searchedStr : xmlRepresentation) { if (xmlString.contains(searchedStr)) { containSearchedStr = true; break; @@ -267,9 +267,9 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { } private CompositeNode prepareIdentityrefData(final String prefix, final boolean valueAsQName) { - MutableCompositeNode cont = NodeFactory.createMutableCompositeNode( + final MutableCompositeNode cont = NodeFactory.createMutableCompositeNode( TestUtils.buildQName("cont", "basic:module", "2013-12-2"), null, null, ModifyAction.CREATE, null); - MutableCompositeNode cont1 = NodeFactory.createMutableCompositeNode( + final MutableCompositeNode cont1 = NodeFactory.createMutableCompositeNode( TestUtils.buildQName("cont1", "basic:module", "2013-12-2"), cont, null, ModifyAction.CREATE, null); cont.getValue().add(cont1); @@ -279,7 +279,7 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { } else { value = "no qname value"; } - MutableSimpleNode lf11 = NodeFactory.createMutableSimpleNode( + final MutableSimpleNode lf11 = NodeFactory.createMutableSimpleNode( TestUtils.buildQName("lf11", "basic:module", "2013-12-2"), cont1, value, ModifyAction.CREATE, null); cont1.getValue().add(lf11); cont1.init(); @@ -289,10 +289,10 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { } private CompositeNode prepareCnStructForYangData(final Object data, final String leafName) { - MutableCompositeNode cont = NodeFactory.createMutableCompositeNode( + final MutableCompositeNode cont = NodeFactory.createMutableCompositeNode( TestUtils.buildQName("cont", "basic:module", "2013-12-2"), null, null, ModifyAction.CREATE, null); - MutableSimpleNode lf1 = NodeFactory.createMutableSimpleNode( + final MutableSimpleNode lf1 = NodeFactory.createMutableSimpleNode( TestUtils.buildQName(leafName, "basic:module", "2013-12-2"), cont, data, ModifyAction.CREATE, null); cont.getValue().add(lf1); cont.init(); @@ -301,12 +301,12 @@ public class CnSnToXmlTest extends YangAndXmlAndDataSchemaLoader { } private CompositeNode prepareLeafrefData() { - MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(TestUtils.buildQName("cont"), null, null, + final MutableCompositeNode cont = NodeFactory.createMutableCompositeNode(TestUtils.buildQName("cont"), null, null, ModifyAction.CREATE, null); - MutableSimpleNode lfBoolean = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfBoolean"), + final MutableSimpleNode lfBoolean = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfBoolean"), cont, Boolean.TRUE, ModifyAction.CREATE, null); - MutableSimpleNode lfLfref = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfLfref"), cont, + final MutableSimpleNode lfLfref = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfLfref"), cont, "true", ModifyAction.CREATE, null); cont.getValue().add(lfBoolean); cont.getValue().add(lfLfref); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java index 15d73e3e7a..7d6da6a94f 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java @@ -87,7 +87,7 @@ public final class TestUtils { private final static YangContextParser parser = new YangParserImpl(); - private static Set loadModules(String resourceDirectory) throws FileNotFoundException { + private static Set loadModules(final String resourceDirectory) throws FileNotFoundException { final File testDir = new File(resourceDirectory); final String[] fileList = testDir.list(); final List testFiles = new ArrayList(); @@ -95,7 +95,7 @@ public final class TestUtils { throw new FileNotFoundException(resourceDirectory); } for (int i = 0; i < fileList.length; i++) { - String fileName = fileList[i]; + final String fileName = fileList[i]; if (new File(testDir, fileName).isDirectory() == false) { testFiles.add(new File(testDir, fileName)); } @@ -103,26 +103,26 @@ public final class TestUtils { return parser.parseYangModels(testFiles); } - public static Set loadModulesFrom(String yangPath) { + public static Set loadModulesFrom(final String yangPath) { try { return TestUtils.loadModules(TestUtils.class.getResource(yangPath).getPath()); - } catch (FileNotFoundException e) { + } catch (final FileNotFoundException e) { LOG.error("Yang files at path: " + yangPath + " weren't loaded."); } return null; } - public static SchemaContext loadSchemaContext(Set modules) { + public static SchemaContext loadSchemaContext(final Set modules) { return parser.resolveSchemaContext(modules); } - public static SchemaContext loadSchemaContext(String resourceDirectory) throws FileNotFoundException { + public static SchemaContext loadSchemaContext(final String resourceDirectory) throws FileNotFoundException { return parser.resolveSchemaContext(loadModulesFrom(resourceDirectory)); } - public static Module findModule(Set modules, String moduleName) { - for (Module module : modules) { + public static Module findModule(final Set modules, final String moduleName) { + for (final Module module : modules) { if (module.getName().equals(moduleName)) { return module; } @@ -130,10 +130,10 @@ public final class TestUtils { return null; } - public static Document loadDocumentFrom(InputStream inputStream) { + public static Document loadDocumentFrom(final InputStream inputStream) { try { - DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); + final DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); + final DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); return docBuilder.parse(inputStream); } catch (SAXException | IOException | ParserConfigurationException e) { LOG.error("Error during loading Document from XML", e); @@ -141,12 +141,12 @@ public final class TestUtils { } } - public static String getDocumentInPrintableForm(Document doc) { + public static String getDocumentInPrintableForm(final Document doc) { Preconditions.checkNotNull(doc); try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer = tf.newTransformer(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final TransformerFactory tf = TransformerFactory.newInstance(); + final Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); @@ -154,10 +154,10 @@ public final class TestUtils { transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, "UTF-8"))); - byte[] charData = out.toByteArray(); + final byte[] charData = out.toByteArray(); return new String(charData, "UTF-8"); } catch (IOException | TransformerException e) { - String msg = "Error during transformation of Document into String"; + final String msg = "Error during transformation of Document into String"; LOG.error(msg, e); return msg; } @@ -170,8 +170,8 @@ public final class TestUtils { * {@code dataSchemaNode}. The method {@link RestconfImpl#createConfigurationData createConfigurationData} is used * because it contains calling of method {code normalizeNode} */ - public static void normalizeCompositeNode(Node node, Set modules, String schemaNodePath) { - RestconfImpl restconf = RestconfImpl.getInstance(); + public static void normalizeCompositeNode(final Node node, final Set modules, final String schemaNodePath) { + final RestconfImpl restconf = RestconfImpl.getInstance(); ControllerContext.getInstance().setSchemas(TestUtils.loadSchemaContext(modules)); prepareMocksForRestconf(modules, restconf); @@ -183,10 +183,10 @@ public final class TestUtils { * module set has only one element then this element is returned. * */ - public static Module resolveModule(String searchedModuleName, Set modules) { + public static Module resolveModule(final String searchedModuleName, final Set modules) { assertNotNull("Modules can't be null.", modules); if (searchedModuleName != null) { - for (Module m : modules) { + for (final Module m : modules) { if (m.getName().equals(searchedModuleName)) { return m; } @@ -197,11 +197,11 @@ public final class TestUtils { return null; } - public static DataSchemaNode resolveDataSchemaNode(String searchedDataSchemaName, Module module) { + public static DataSchemaNode resolveDataSchemaNode(final String searchedDataSchemaName, final Module module) { assertNotNull("Module can't be null", module); if (searchedDataSchemaName != null) { - for (DataSchemaNode dsn : module.getChildNodes()) { + for (final DataSchemaNode dsn : module.getChildNodes()) { if (dsn.getQName().getLocalName().equals(searchedDataSchemaName)) { return dsn; } @@ -212,39 +212,39 @@ public final class TestUtils { return null; } - public static QName buildQName(String name, String uri, String date, String prefix) { + public static QName buildQName(final String name, final String uri, final String date, final String prefix) { try { - URI u = new URI(uri); + final URI u = new URI(uri); Date dt = null; if (date != null) { dt = Date.valueOf(date); } - return new QName(u, dt, prefix, name); - } catch (URISyntaxException e) { + return QName.create(u, dt, name); + } catch (final URISyntaxException e) { return null; } } - public static QName buildQName(String name, String uri, String date) { + public static QName buildQName(final String name, final String uri, final String date) { return buildQName(name, uri, date, null); } - public static QName buildQName(String name) { + public static QName buildQName(final String name) { return buildQName(name, "", null); } - private static void addDummyNamespaceToAllNodes(NodeWrapper wrappedNode) throws URISyntaxException { + private static void addDummyNamespaceToAllNodes(final NodeWrapper wrappedNode) throws URISyntaxException { wrappedNode.setNamespace(new URI("")); if (wrappedNode instanceof CompositeNodeWrapper) { - for (NodeWrapper childNodeWrapper : ((CompositeNodeWrapper) wrappedNode).getValues()) { + for (final NodeWrapper childNodeWrapper : ((CompositeNodeWrapper) wrappedNode).getValues()) { addDummyNamespaceToAllNodes(childNodeWrapper); } } } - private static void prepareMocksForRestconf(Set modules, RestconfImpl restconf) { - ControllerContext controllerContext = ControllerContext.getInstance(); - BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class); + private static void prepareMocksForRestconf(final Set modules, final RestconfImpl restconf) { + final ControllerContext controllerContext = ControllerContext.getInstance(); + final BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class); controllerContext.setSchemas(TestUtils.loadSchemaContext(modules)); @@ -255,10 +255,10 @@ public final class TestUtils { restconf.setBroker(mockedBrokerFacade); } - public static Node readInputToCnSn(String path, boolean dummyNamespaces, - MessageBodyReader> reader) throws WebApplicationException { + public static Node readInputToCnSn(final String path, final boolean dummyNamespaces, + final MessageBodyReader> reader) throws WebApplicationException { - InputStream inputStream = TestUtils.class.getResourceAsStream(path); + final InputStream inputStream = TestUtils.class.getResourceAsStream(path); try { final Node node = reader.readFrom(null, null, null, null, null, inputStream); assertTrue(node instanceof CompositeNodeWrapper); @@ -266,13 +266,13 @@ public final class TestUtils { try { TestUtils.addDummyNamespaceToAllNodes((CompositeNodeWrapper) node); return ((CompositeNodeWrapper) node).unwrap(); - } catch (URISyntaxException e) { + } catch (final URISyntaxException e) { LOG.error(e.getMessage()); assertTrue(e.getMessage(), false); } } return node; - } catch (IOException e) { + } catch (final IOException e) { LOG.error(e.getMessage()); assertTrue(e.getMessage(), false); } @@ -290,17 +290,17 @@ public final class TestUtils { // return null; // } - public static Node readInputToCnSn(String path, MessageBodyReader> reader) { + public static Node readInputToCnSn(final String path, final MessageBodyReader> reader) { return readInputToCnSn(path, false, reader); } - public static String writeCompNodeWithSchemaContextToOutput(Node node, Set modules, - DataSchemaNode dataSchemaNode, MessageBodyWriter messageBodyWriter) throws IOException, + public static String writeCompNodeWithSchemaContextToOutput(final Node node, final Set modules, + final DataSchemaNode dataSchemaNode, final MessageBodyWriter messageBodyWriter) throws IOException, WebApplicationException { assertNotNull(dataSchemaNode); assertNotNull("Composite node can't be null", node); - ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); + final ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); ControllerContext.getInstance().setSchemas(loadSchemaContext(modules)); @@ -311,12 +311,12 @@ public final class TestUtils { return byteArrayOS.toString(); } - public static String loadTextFile(String filePath) throws IOException { - FileReader fileReader = new FileReader(filePath); - BufferedReader bufReader = new BufferedReader(fileReader); + public static String loadTextFile(final String filePath) throws IOException { + final FileReader fileReader = new FileReader(filePath); + final BufferedReader bufReader = new BufferedReader(fileReader); String line = null; - StringBuilder result = new StringBuilder(); + final StringBuilder result = new StringBuilder(); while ((line = bufReader.readLine()) != null) { result.append(line); } @@ -324,10 +324,10 @@ public final class TestUtils { return result.toString(); } - private static Pattern patternForStringsSeparatedByWhiteChars(String... substrings) { - StringBuilder pattern = new StringBuilder(); + private static Pattern patternForStringsSeparatedByWhiteChars(final String... substrings) { + final StringBuilder pattern = new StringBuilder(); pattern.append(".*"); - for (String substring : substrings) { + for (final String substring : substrings) { pattern.append(substring); pattern.append("\\s*"); } @@ -335,15 +335,15 @@ public final class TestUtils { return Pattern.compile(pattern.toString(), Pattern.DOTALL); } - public static boolean containsStringData(String jsonOutput, String... substrings) { - Pattern pattern = patternForStringsSeparatedByWhiteChars(substrings); - Matcher matcher = pattern.matcher(jsonOutput); + public static boolean containsStringData(final String jsonOutput, final String... substrings) { + final Pattern pattern = patternForStringsSeparatedByWhiteChars(substrings); + final Matcher matcher = pattern.matcher(jsonOutput); return matcher.matches(); } public static NormalizedNode compositeNodeToDatastoreNormalizedNode(final CompositeNode compositeNode, final DataSchemaNode schema) { - List> lst = new ArrayList>(); + final List> lst = new ArrayList>(); lst.add(compositeNode); if (schema instanceof ContainerSchemaNode) { return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser() @@ -359,15 +359,15 @@ public final class TestUtils { "It wasn't possible to translate specified data to datastore readable form.")); } - public static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(String localName, String namespace, - String revision) throws ParseException { + public static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(final String localName, final String namespace, + final String revision) throws ParseException { return new YangInstanceIdentifier.NodeIdentifier(QName.create(namespace, revision, localName)); } - public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(String localName, - String namespace, String revision, Map keys) throws ParseException { - Map predicate = new HashMap<>(); - for (String key : keys.keySet()) { + public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName, + final String namespace, final String revision, final Map keys) throws ParseException { + final Map predicate = new HashMap<>(); + for (final String key : keys.keySet()) { predicate.put(QName.create(namespace, revision, key), keys.get(key)); } @@ -376,13 +376,13 @@ public final class TestUtils { QName.create(namespace, revision, localName), predicate); } - public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(String localName, - String namespace, String revision, String... keysAndValues) throws ParseException { - java.util.Date date = new SimpleDateFormat("yyyy-MM-dd").parse(revision); + public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName, + final String namespace, final String revision, final String... keysAndValues) throws ParseException { + final java.util.Date date = new SimpleDateFormat("yyyy-MM-dd").parse(revision); if (keysAndValues.length % 2 != 0) { new IllegalArgumentException("number of keys argument have to be divisible by 2 (map)"); } - Map predicate = new HashMap<>(); + final Map predicate = new HashMap<>(); int i = 0; while (i < keysAndValues.length) { @@ -394,7 +394,7 @@ public final class TestUtils { } public static CompositeNode prepareCompositeNodeWithIetfInterfacesInterfacesData() { - CompositeNodeBuilder interfaceBuilder = ImmutableCompositeNode.builder(); + final CompositeNodeBuilder interfaceBuilder = ImmutableCompositeNode.builder(); interfaceBuilder.addLeaf(buildQName("name", "dummy", "2014-07-29"), "eth0"); interfaceBuilder.addLeaf(buildQName("type", "dummy", "2014-07-29"), "ethernetCsmacd"); interfaceBuilder.addLeaf(buildQName("enabled", "dummy", "2014-07-29"), "false"); @@ -403,11 +403,11 @@ public final class TestUtils { } static NormalizedNode prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException { - String ietfInterfacesDate = "2013-07-04"; - String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces"; - DataContainerNodeAttrBuilder mapEntryNode = ImmutableMapEntryNodeBuilder.create(); + final String ietfInterfacesDate = "2013-07-04"; + final String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + final DataContainerNodeAttrBuilder mapEntryNode = ImmutableMapEntryNodeBuilder.create(); - Map predicates = new HashMap<>(); + final Map predicates = new HashMap<>(); predicates.put("name", "eth0"); mapEntryNode.withNodeIdentifier(getNodeIdentifierPredicate("interface", namespace, ietfInterfacesDate, diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java index 9bafe9760a..31a7661b2c 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java @@ -9,11 +9,11 @@ package org.opendaylight.controller.netconf.client; import io.netty.channel.Channel; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; import java.util.Collection; +import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSession; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToXMLEncoder; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfXMLToMessageDecoder; import org.slf4j.Logger; @@ -49,10 +49,10 @@ public class NetconfClientSession extends AbstractNetconfSession encoder) { // TODO used only in negotiator, client supports only auto start-exi - replaceMessageDecoder(new NetconfEXIToMessageDecoder(exiCodec)); - replaceMessageEncoder(new NetconfMessageToEXIEncoder(exiCodec)); + replaceMessageDecoder(decoder); + replaceMessageEncoder(encoder); } @Override diff --git a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java index 850ad55fc6..2a3ecf2c04 100644 --- a/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java +++ b/opendaylight/netconf/netconf-client/src/test/java/org/opendaylight/controller/netconf/client/NetconfClientSessionTest.java @@ -12,7 +12,6 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; - import com.google.common.collect.Lists; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -24,6 +23,8 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder; import org.openexi.proc.common.EXIOptions; public class NetconfClientSessionTest { @@ -53,7 +54,9 @@ public class NetconfClientSessionTest { Mockito.doReturn("").when(channelHandler).toString(); NetconfClientSession session = new NetconfClientSession(sessionListener, channel, sessId, caps); - session.addExiHandlers(codec); + final NetconfMessageToEXIEncoder exiEncoder = NetconfMessageToEXIEncoder.create(codec); + final NetconfEXIToMessageDecoder exiDecoder = NetconfEXIToMessageDecoder.create(codec); + session.addExiHandlers(exiDecoder, exiEncoder); session.stopExiCommunication(); assertEquals(caps, session.getServerCapabilities()); diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java index ca604a4f65..8f2c39df06 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java @@ -10,15 +10,15 @@ package org.opendaylight.controller.netconf.impl; import com.google.common.base.Preconditions; import io.netty.channel.Channel; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSession; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToXMLEncoder; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfXMLToMessageDecoder; import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; @@ -46,8 +46,8 @@ public final class NetconfServerSession extends AbstractNetconfSession getTransportForString(String transport) { + private Class getTransportForString(final String transport) { switch(transport) { case "ssh" : return NetconfSsh.class; @@ -120,7 +120,7 @@ public final class NetconfServerSession extends AbstractNetconfSession encoder) { + replaceMessageDecoder(decoder); + replaceMessageEncoderAfterNextMessage(encoder); } @Override diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java index 1a35e4d6ba..26abdd974d 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshotImpl.java @@ -8,8 +8,8 @@ package org.opendaylight.controller.netconf.impl.osgi; -import java.util.Collections; -import java.util.HashSet; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import java.util.Set; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; @@ -21,17 +21,15 @@ public class NetconfOperationServiceSnapshotImpl implements NetconfOperationServ private final Set services; private final String netconfSessionIdForReporting; - public NetconfOperationServiceSnapshotImpl(Set factories, String sessionIdForReporting) { - Set services = new HashSet<>(); + public NetconfOperationServiceSnapshotImpl(final Set factories, final String sessionIdForReporting) { + final Builder b = ImmutableSet.builder(); netconfSessionIdForReporting = sessionIdForReporting; for (NetconfOperationServiceFactory factory : factories) { - services.add(factory.createService(netconfSessionIdForReporting)); + b.add(factory.createService(netconfSessionIdForReporting)); } - this.services = Collections.unmodifiableSet(services); + this.services = b.build(); } - - @Override public String getNetconfSessionIdForReporting() { return netconfSessionIdForReporting; diff --git a/opendaylight/netconf/netconf-it/pom.xml b/opendaylight/netconf/netconf-it/pom.xml index 7218ac7872..6e4c16d20f 100644 --- a/opendaylight/netconf/netconf-it/pom.xml +++ b/opendaylight/netconf/netconf-it/pom.xml @@ -122,6 +122,12 @@ netconf-ssh test + + ${project.groupId} + netconf-testtool + ${project.version} + test + ${project.groupId} netconf-ssh diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index 5f316d11c1..bd8e2ed4df 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -41,8 +41,6 @@ import org.apache.sshd.server.session.ServerSession; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.auth.AuthProvider; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; @@ -116,7 +114,7 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { @Test public void testSecure() throws Exception { final NetconfClientDispatcher dispatch = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); - try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, getClientConfiguration(new SimpleNetconfClientSessionListener()))) { + try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, getClientConfiguration(new SimpleNetconfClientSessionListener(), TLS_ADDRESS))) { NetconfMessage response = netconfClient.sendMessage(getGetConfig()); assertFalse("Unexpected error message " + XmlUtil.toString(response.getDocument()), NetconfMessageUtil.isErrorMessage(response)); @@ -143,7 +141,7 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { final NetconfClientDispatcher dispatch = new NetconfClientDispatcherImpl(getNettyThreadgroup(), getNettyThreadgroup(), getHashedWheelTimer()); final NetconfDeviceCommunicator sessionListener = getSessionListener(); - try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, getClientConfiguration(sessionListener))) { + try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, getClientConfiguration(sessionListener, TLS_ADDRESS))) { final AtomicInteger responseCounter = new AtomicInteger(0); final List>> futures = Lists.newArrayList(); @@ -182,15 +180,15 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { } } - private NetconfMessage changeMessageId(final NetconfMessage getConfig, final int i) throws IOException, SAXException { + public static NetconfMessage changeMessageId(final NetconfMessage getConfig, final int i) throws IOException, SAXException { String s = XmlUtil.toString(getConfig.getDocument(), false); s = s.replace("101", Integer.toString(i)); return new NetconfMessage(XmlUtil.readXmlToDocument(s)); } - public NetconfClientConfiguration getClientConfiguration(final NetconfClientSessionListener sessionListener) throws IOException { + static NetconfClientConfiguration getClientConfiguration(final NetconfClientSessionListener sessionListener,final InetSocketAddress tlsAddress) throws IOException { final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create(); - b.withAddress(TLS_ADDRESS); + b.withAddress(tlsAddress); // Using session listener from sal-netconf-connector since stress test cannot be performed with simple listener b.withSessionListener(sessionListener); b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000)); @@ -200,11 +198,8 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { return b.build(); } - @Mock - private RemoteDevice mockedRemoteDevice; - - private NetconfDeviceCommunicator getSessionListener() { - MockitoAnnotations.initMocks(this); + static NetconfDeviceCommunicator getSessionListener() { + RemoteDevice mockedRemoteDevice = mock(RemoteDevice.class); doNothing().when(mockedRemoteDevice).onRemoteSessionUp(any(NetconfSessionCapabilities.class), any(RemoteDeviceCommunicator.class)); doNothing().when(mockedRemoteDevice).onRemoteSessionDown(); return new NetconfDeviceCommunicator(new RemoteDeviceId("secure-test"), mockedRemoteDevice); @@ -217,7 +212,7 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest { return mockAuth; } - public AuthenticationHandler getAuthHandler() throws IOException { + public static AuthenticationHandler getAuthHandler() throws IOException { return new LoginPassword(USERNAME, PASSWORD); } diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTestTool.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTestTool.java new file mode 100644 index 0000000000..02472733ba --- /dev/null +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTestTool.java @@ -0,0 +1,192 @@ +/* + * 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.html + */ + +package org.opendaylight.controller.netconf.it; + +import static java.lang.Thread.sleep; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.opendaylight.controller.netconf.it.NetconfITSecureTest.getSessionListener; + +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.HashedWheelTimer; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; +import org.opendaylight.controller.netconf.client.TestingNetconfClient; +import org.opendaylight.controller.netconf.test.tool.Main.Params; +import org.opendaylight.controller.netconf.test.tool.NetconfDeviceSimulator; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcResult; + +public class NetconfITSecureTestTool +{ + + //set up port both for testool device and test + public static final int PORT = 17833; + private static final InetSocketAddress TLS_ADDRESS = new InetSocketAddress("127.0.0.1", PORT); + + private String xmlFile = "netconfMessages/editConfig.xml"; + + private ExecutorService msgExec = Executors.newFixedThreadPool(8); + + Collection> tasks = new LinkedList>(); + + final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator(); + + @Before + public void setUp() throws Exception { + + //Set up parameters for testtool device + Params params = new Params(); + params.debug = true; + params.deviceCount = 1; + params.startingPort = PORT; + params.ssh = true; + params.exi = true; + + final List openDevices = netconfDeviceSimulator.start(params); + } + + @After + public void tearDown() throws Exception { + + } + + /** + * Test all requests are handled properly and no mismatch occurs in listener + */ + @Test(timeout = 6*60*1000) + public void testSecureStress() throws Exception { + + final int requests = 4000; + + List> tasks = new ArrayList<>(); + + final NetconfClientDispatcher dispatch = new NetconfClientDispatcherImpl(new NioEventLoopGroup(), new NioEventLoopGroup(), new HashedWheelTimer()); + + final NetconfDeviceCommunicator sessionListener = getSessionListener(); + + try (TestingNetconfClient netconfClient = new TestingNetconfClient("testing-ssh-client", dispatch, NetconfITSecureTest.getClientConfiguration(sessionListener, TLS_ADDRESS));) + { + + final AtomicInteger responseCounter = new AtomicInteger(0); + final List>> futures = Lists.newArrayList(); + + for (int i = 0; i < requests; i++) { + + NetconfMessage getConfig = XmlFileLoader.xmlFileToNetconfMessage(xmlFile); + + getConfig = NetconfITSecureTest.changeMessageId(getConfig,i); + + Runnable worker = new NetconfITSecureTestToolRunnable(getConfig,i, sessionListener, futures, responseCounter); + + tasks.add(msgExec.submit(worker)); + + } + + msgExec.shutdown(); + + // Wait for every future + for (final Future task : tasks){ + try + { + + task.get(3, TimeUnit.MINUTES); + } catch (final TimeoutException e) { + fail(String.format("Request %d is not responding", tasks.indexOf(task))); + } + } + + for (final ListenableFuture> future : futures) { + try { + + future.get(3, TimeUnit.MINUTES); + } catch (final TimeoutException e) { + fail(String.format("Reply %d is not responding", futures.indexOf(future))); + } + } + + sleep(5000); + + assertEquals(requests, responseCounter.get()); + + } + } + + class NetconfITSecureTestToolRunnable implements Runnable { + + private NetconfMessage getConfig; + private int it; + private NetconfDeviceCommunicator sessionListener; + private List>> futures; + private AtomicInteger responseCounter; + + public NetconfITSecureTestToolRunnable(NetconfMessage getConfig, int it, NetconfDeviceCommunicator sessionListener, List>> futures, AtomicInteger responseCounter){ + this.getConfig = getConfig; + this.it = it; + this.sessionListener = sessionListener; + this.futures = futures; + this.responseCounter = responseCounter; + } + + @Override + public void run(){ + + ListenableFuture> netconfMessageFuture; + + netconfMessageFuture = sessionListener.sendRequest(getConfig, QName.create("namespace", "2012-12-12", "get")); + + futures.add(netconfMessageFuture); + Futures.addCallback(netconfMessageFuture, new FutureCallback>() { + + @Override + public void onSuccess(final RpcResult result) { + + if(result.isSuccessful()&result.getErrors().isEmpty()) { + responseCounter.incrementAndGet(); + } else { + + fail(String.format("Message result not ok %s", result.getErrors().toString())); + + } + } + + @Override + public void onFailure(final Throwable t) { + + fail(String.format("Message failed %s", Throwables.getStackTraceAsString(t))); + + } + } + ); + } + } + +} diff --git a/opendaylight/netconf/netconf-it/src/test/resources/logback-test.xml b/opendaylight/netconf/netconf-it/src/test/resources/logback-test.xml index 91fb805e6a..e9ee13d8b6 100644 --- a/opendaylight/netconf/netconf-it/src/test/resources/logback-test.xml +++ b/opendaylight/netconf/netconf-it/src/test/resources/logback-test.xml @@ -6,8 +6,8 @@ - - + + diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSession.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSession.java index fd11ce8c51..a59b1a0d76 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSession.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSession.java @@ -10,6 +10,8 @@ package org.opendaylight.controller.netconf.nettyutil; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; import java.io.IOException; import org.opendaylight.controller.netconf.api.NetconfExiSession; import org.opendaylight.controller.netconf.api.NetconfMessage; @@ -17,10 +19,13 @@ import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.NetconfSessionListener; import org.opendaylight.controller.netconf.api.NetconfTerminationReason; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder; +import org.opendaylight.controller.netconf.nettyutil.handler.NetconfMessageToEXIEncoder; import org.opendaylight.controller.netconf.nettyutil.handler.exi.EXIParameters; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.protocol.framework.AbstractProtocolSession; import org.openexi.proc.common.EXIOptionsException; +import org.openexi.sax.TransmogrifierException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -117,12 +122,35 @@ public abstract class AbstractNetconfSession encoder); public final boolean isUp() { return up; diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXICodec.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXICodec.java index 98baef0f85..16da7a7f9d 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXICodec.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXICodec.java @@ -1,6 +1,9 @@ package org.opendaylight.controller.netconf.nettyutil.handler; import com.google.common.base.Preconditions; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import org.openexi.proc.HeaderOptionsOutputType; import org.openexi.proc.common.EXIOptions; import org.openexi.proc.common.EXIOptionsException; @@ -8,6 +11,9 @@ import org.openexi.proc.common.GrammarOptions; import org.openexi.proc.grammars.GrammarCache; import org.openexi.sax.EXIReader; import org.openexi.sax.Transmogrifier; +import org.openexi.sax.TransmogrifierException; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; public final class NetconfEXICodec { /** @@ -16,13 +22,41 @@ public final class NetconfEXICodec { * of the stream. This is really useful, so let's output it now. */ private static final boolean OUTPUT_EXI_COOKIE = true; + /** + * OpenEXI does not allow us to directly prevent resolution of external entities. In order + * to prevent XXE attacks, we reuse a single no-op entity resolver. + */ + private static final EntityResolver ENTITY_RESOLVER = new EntityResolver() { + @Override + public InputSource resolveEntity(final String publicId, final String systemId) { + return new InputSource(); + } + }; + + /** + * Since we have a limited number of options we can have, instantiating a weak cache + * will allow us to reuse instances where possible. + */ + private static final LoadingCache GRAMMAR_CACHES = CacheBuilder.newBuilder().weakValues().build(new CacheLoader() { + @Override + public GrammarCache load(final Short key) { + return new GrammarCache(key); + } + }); + + /** + * Grammar cache acts as a template and is duplicated by the Transmogrifier and the Reader + * before use. It is safe to reuse a single instance. + */ + private final GrammarCache exiGrammarCache; private final EXIOptions exiOptions; public NetconfEXICodec(final EXIOptions exiOptions) { this.exiOptions = Preconditions.checkNotNull(exiOptions); + this.exiGrammarCache = createGrammarCache(exiOptions); } - private GrammarCache getGrammarCache() { + private static GrammarCache createGrammarCache(final EXIOptions exiOptions) { short go = GrammarOptions.DEFAULT_OPTIONS; if (exiOptions.getPreserveComments()) { go = GrammarOptions.addCM(go); @@ -37,23 +71,25 @@ public final class NetconfEXICodec { go = GrammarOptions.addPI(go); } - return new GrammarCache(null, go); + return GRAMMAR_CACHES.getUnchecked(go); } EXIReader getReader() throws EXIOptionsException { final EXIReader r = new EXIReader(); r.setPreserveLexicalValues(exiOptions.getPreserveLexicalValues()); - r.setGrammarCache(getGrammarCache()); + r.setGrammarCache(exiGrammarCache); + r.setEntityResolver(ENTITY_RESOLVER); return r; } - Transmogrifier getTransmogrifier() throws EXIOptionsException { + Transmogrifier getTransmogrifier() throws EXIOptionsException, TransmogrifierException { final Transmogrifier transmogrifier = new Transmogrifier(); transmogrifier.setAlignmentType(exiOptions.getAlignmentType()); transmogrifier.setBlockSize(exiOptions.getBlockSize()); - transmogrifier.setGrammarCache(getGrammarCache()); + transmogrifier.setGrammarCache(exiGrammarCache); transmogrifier.setOutputCookie(OUTPUT_EXI_COOKIE); transmogrifier.setOutputOptions(HeaderOptionsOutputType.all); + transmogrifier.setResolveExternalGeneralEntities(false); return transmogrifier; } } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIToMessageDecoder.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIToMessageDecoder.java index 77d33e1831..db265dee40 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIToMessageDecoder.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIToMessageDecoder.java @@ -33,11 +33,19 @@ public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder { private static final Logger LOG = LoggerFactory.getLogger(NetconfEXIToMessageDecoder.class); private static final SAXTransformerFactory FACTORY = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); + /** + * This class is not marked as shared, so it can be attached to only a single channel, + * which means that {@link #decode(ChannelHandlerContext, ByteBuf, List)} + * cannot be invoked concurrently. Hence we can reuse the reader. + */ + private final EXIReader reader; - private final NetconfEXICodec codec; + private NetconfEXIToMessageDecoder(final EXIReader reader) { + this.reader = Preconditions.checkNotNull(reader); + } - public NetconfEXIToMessageDecoder(final NetconfEXICodec codec) { - this.codec = Preconditions.checkNotNull(codec); + public static NetconfEXIToMessageDecoder create(final NetconfEXICodec codec) throws EXIOptionsException { + return new NetconfEXIToMessageDecoder(codec.getReader()); } @Override @@ -59,15 +67,15 @@ public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder { LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in)); } - final EXIReader r = codec.getReader(); final TransformerHandler handler = FACTORY.newTransformerHandler(); - r.setContentHandler(handler); + reader.setContentHandler(handler); final DOMResult domResult = new DOMResult(); handler.setResult(domResult); try (final InputStream is = new ByteBufInputStream(in)) { - r.parse(new InputSource(is)); + // Performs internal reset before doing anything + reader.parse(new InputSource(is)); } out.add(new NetconfMessage((Document) domResult.getNode())); diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java index f1e72ed85f..aceb6ac520 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java @@ -14,32 +14,49 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import java.io.IOException; import java.io.OutputStream; +import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.openexi.proc.common.EXIOptionsException; import org.openexi.sax.Transmogrifier; +import org.openexi.sax.TransmogrifierException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.xml.sax.ContentHandler; public final class NetconfMessageToEXIEncoder extends MessageToByteEncoder { private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageToEXIEncoder.class); - private final NetconfEXICodec codec; + /** + * This class is not marked as shared, so it can be attached to only a single channel, + * which means that {@link #encode(ChannelHandlerContext, NetconfMessage, ByteBuf)} + * cannot be invoked concurrently. Hence we can reuse the transmogrifier. + */ + private final Transmogrifier transmogrifier; - public NetconfMessageToEXIEncoder(final NetconfEXICodec codec) { - this.codec = Preconditions.checkNotNull(codec); + private NetconfMessageToEXIEncoder(final Transmogrifier transmogrifier) { + this.transmogrifier = Preconditions.checkNotNull(transmogrifier); + } + + public static NetconfMessageToEXIEncoder create(final NetconfEXICodec codec) throws EXIOptionsException, TransmogrifierException { + return new NetconfMessageToEXIEncoder(codec.getTransmogrifier()); } @Override - protected void encode(final ChannelHandlerContext ctx, final NetconfMessage msg, final ByteBuf out) throws EXIOptionsException, IOException, TransformerException { + protected void encode(final ChannelHandlerContext ctx, final NetconfMessage msg, final ByteBuf out) throws EXIOptionsException, IOException, TransformerException, TransmogrifierException { LOG.trace("Sent to encode : {}", msg); try (final OutputStream os = new ByteBufOutputStream(out)) { - final Transmogrifier transmogrifier = codec.getTransmogrifier(); transmogrifier.setOutputStream(os); - - ThreadLocalTransformers.getDefaultTransformer().transform(new DOMSource(msg.getDocument()), new SAXResult(transmogrifier.getSAXTransmogrifier())); + final ContentHandler handler = transmogrifier.getSAXTransmogrifier(); + final Transformer transformer = ThreadLocalTransformers.getDefaultTransformer(); + transformer.transform(new DOMSource(msg.getDocument()), new SAXResult(handler)); + } finally { + // Make sure we do not retain any reference to state by removing + // the output stream reference and resetting internal state. + transmogrifier.setOutputStream(null); + transmogrifier.getSAXTransmogrifier(); } } } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerWriter.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerWriter.java index 1e976ce6a1..757dc1a2ba 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerWriter.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerWriter.java @@ -62,12 +62,15 @@ public final class AsyncSshHandlerWriter implements AutoCloseable { return; } - writeWithPendingDetection(ctx, promise, byteBufMsg); + writeWithPendingDetection(ctx, promise, byteBufMsg, false); } } - private void writeWithPendingDetection(final ChannelHandlerContext ctx, final ChannelPromise promise, final ByteBuf byteBufMsg) { + //sending message with pending + //if resending message not succesfull, then attribute wasPending is true + private void writeWithPendingDetection(final ChannelHandlerContext ctx, final ChannelPromise promise, final ByteBuf byteBufMsg, boolean wasPending) { try { + if (LOG.isTraceEnabled()) { LOG.trace("Writing request on channel: {}, message: {}", ctx.channel(), byteBufToString(byteBufMsg)); } @@ -77,7 +80,7 @@ public final class AsyncSshHandlerWriter implements AutoCloseable { public void operationComplete(final IoWriteFuture future) { if (LOG.isTraceEnabled()) { LOG.trace("Ssh write request finished on channel: {} with result: {}: and ex:{}, message: {}", - ctx.channel(), future.isWritten(), future.getException(), byteBufToString(byteBufMsg)); + ctx.channel(), future.isWritten(), future.getException(), byteBufToString(byteBufMsg)); } // Notify success or failure @@ -94,30 +97,40 @@ public final class AsyncSshHandlerWriter implements AutoCloseable { // Check pending queue and schedule next // At this time we are guaranteed that we are not in pending state anymore so the next request should succeed writePendingIfAny(); + } }); + + //rescheduling message from queue after successfully sent + if(wasPending){ + byteBufMsg.resetReaderIndex(); + pending.remove(); + } + } catch (final WritePendingException e) { - queueRequest(ctx, byteBufMsg, promise); + + if(wasPending == false){ + queueRequest(ctx, byteBufMsg, promise); + } } } private synchronized void writePendingIfAny() { + if (pending.peek() == null) { return; } - // In case of pending, reschedule next message from queue - final PendingWriteRequest pendingWrite = pending.poll(); + final PendingWriteRequest pendingWrite = pending.peek(); final ByteBuf msg = pendingWrite.msg; if (LOG.isTraceEnabled()) { LOG.trace("Writing pending request on channel: {}, message: {}", pendingWrite.ctx.channel(), byteBufToString(msg)); } - writeWithPendingDetection(pendingWrite.ctx, pendingWrite.promise, msg); + writeWithPendingDetection(pendingWrite.ctx, pendingWrite.promise, msg, true); } public static String byteBufToString(final ByteBuf msg) { - msg.resetReaderIndex(); final String s = msg.toString(Charsets.UTF_8); msg.resetReaderIndex(); return s; @@ -144,6 +157,7 @@ public final class AsyncSshHandlerWriter implements AutoCloseable { private Buffer toBuffer(final ByteBuf msg) { // TODO Buffer vs ByteBuf translate, Can we handle that better ? + msg.resetReaderIndex(); final byte[] temp = new byte[msg.readableBytes()]; msg.readBytes(temp, 0, msg.readableBytes()); return new Buffer(temp); diff --git a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionTest.java b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionTest.java index 7946afdbf5..3a1b26dd9e 100644 --- a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionTest.java +++ b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/AbstractNetconfSessionTest.java @@ -19,12 +19,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; - import com.google.common.base.Optional; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; import java.util.Collections; import org.junit.Before; import org.junit.Test; @@ -34,7 +35,6 @@ import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.NetconfSession; import org.opendaylight.controller.netconf.api.NetconfSessionListener; import org.opendaylight.controller.netconf.api.NetconfTerminationReason; -import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; import org.opendaylight.controller.netconf.nettyutil.handler.exi.NetconfStartExiMessage; import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage; import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; @@ -115,7 +115,7 @@ public class AbstractNetconfSessionTest { testingNetconfSession = spy(testingNetconfSession); testingNetconfSession.startExiCommunication(NetconfStartExiMessage.create(new EXIOptions(), "4")); - verify(testingNetconfSession).addExiHandlers(any(NetconfEXICodec.class)); + verify(testingNetconfSession).addExiHandlers(any(ByteToMessageDecoder.class), any(MessageToByteEncoder.class)); } @Test @@ -148,7 +148,7 @@ public class AbstractNetconfSessionTest { } @Override - protected void addExiHandlers(final NetconfEXICodec exiCodec) {} + protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder encoder) {} @Override public void stopExiCommunication() {} diff --git a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIHandlersTest.java b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIHandlersTest.java index 4f804abfe8..666da8ad46 100644 --- a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIHandlersTest.java +++ b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfEXIHandlersTest.java @@ -10,7 +10,6 @@ package org.opendaylight.controller.netconf.nettyutil.handler; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; - import com.google.common.collect.Lists; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -41,8 +40,8 @@ public class NetconfEXIHandlersTest { @Before public void setUp() throws Exception { final NetconfEXICodec codec = new NetconfEXICodec(new EXIOptions()); - netconfMessageToEXIEncoder = new NetconfMessageToEXIEncoder(codec); - netconfEXIToMessageDecoder = new NetconfEXIToMessageDecoder(codec); + netconfMessageToEXIEncoder = NetconfMessageToEXIEncoder.create(codec); + netconfEXIToMessageDecoder = NetconfEXIToMessageDecoder.create(codec); msg = new NetconfMessage(XmlUtil.readXmlToDocument(msgAsString)); this.msgAsExi = msgToExi(msgAsString, codec); diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java index e441c709cc..e52fce7ae0 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java @@ -46,7 +46,7 @@ public final class Main { private static final Logger LOG = LoggerFactory.getLogger(Main.class); - static class Params { + public static class Params { @Arg(dest = "schemas-dir") public File schemasDir; diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml index 5dd5a90f7d..a0458c45e3 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml @@ -1,4 +1,4 @@ - + diff --git a/pom.xml b/pom.xml index 87a69e00c9..3bfffaa5d4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,12 +7,16 @@ 1.1.0-SNAPSHOT opendaylight/commons/parent + releasepom 0.2.0-SNAPSHOT pom + controller + 3.0 +