</properties>
<dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>networkconfig.neutron</artifactId>
- </dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>features-test</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>features-test</artifactId>
- <version>0.7.0-SNAPSHOT</version>
- </dependency>
</dependencies>
<build>
<?xml version="1.0" encoding="UTF-8"?>
-<project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<?xml version="1.0" encoding="UTF-8"?>
-<project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<?xml version="1.0" encoding="UTF-8"?>
-<project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<?xml version="1.0" encoding="UTF-8"?>
-<project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<?xml version="1.0" encoding="UTF-8"?>
-<project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
-<?xml version="1.0" encoding="UTF-8"?>\r
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <artifactId>${artifactId}</artifactId>\r
- <groupId>${groupId}</groupId>\r
- <version>${version}</version>\r
- <packaging>bundle</packaging>\r
- <properties>\r
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\r
- <nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>\r
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>${artifactId}</artifactId>
+ <groupId>${groupId}</groupId>
+ <version>${version}</version>
+ <packaging>bundle</packaging>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>
<nexus.repository.release>opendaylight.release</nexus.repository.release>
- <nexus.repository.snapshot>opendaylight.release</nexus.repository.snaphot>
- <yang.version>0.7.0-SNAPSHOT</yang.version>\r
- <yang.codegen.version>0.7.0-SNAPSHOT</yang.codegen.version>\r
- <bundle.plugin.version>2.3.7</bundle.plugin.version>\r
- </properties>\r
- <scm>\r
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>\r
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>\r
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>\r
- </scm>\r
-\r
- <build>\r
- <plugins>\r
- <plugin>\r
- <groupId>org.apache.felix</groupId>\r
- <artifactId>maven-bundle-plugin</artifactId>\r
- <version>${bundle.plugin.version}</version>\r
- <extensions>true</extensions>\r
- <configuration>\r
- <instructions>\r
- <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>\r
- </instructions>\r
- <manifestLocation>${project.basedir}/META-INF</manifestLocation>\r
- </configuration>\r
- </plugin>\r
- <plugin>\r
- <groupId>org.apache.maven.plugins</groupId>\r
- <artifactId>maven-compiler-plugin</artifactId>\r
- <version>2.5.1</version>\r
- <inherited>true</inherited>\r
- <configuration>\r
- <source>1.7</source>\r
- <target>1.7</target>\r
- </configuration>\r
- </plugin>\r
- <plugin>\r
- <groupId>org.apache.maven.plugins</groupId>\r
- <artifactId>maven-javadoc-plugin</artifactId>\r
- <version>2.8.1</version>\r
- <configuration>\r
- <stylesheet>maven</stylesheet>\r
- </configuration>\r
- <executions>\r
- <execution>\r
- <goals>\r
- <goal>aggregate</goal>\r
- </goals>\r
- <phase>site</phase>\r
- </execution>\r
- </executions>\r
- </plugin>\r
- <plugin>\r
- <groupId>org.opendaylight.yangtools</groupId>\r
- <artifactId>yang-maven-plugin</artifactId>\r
- <version>${yang.version}</version>\r
- <executions>\r
- <execution>\r
- <goals>\r
- <goal>generate-sources</goal>\r
- </goals>\r
- <configuration>\r
- <yangFilesRootDir>src/main/yang</yangFilesRootDir>\r
- <codeGenerators>\r
- <generator>\r
- <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>\r
- <outputBaseDir>target/generated-sources/sal</outputBaseDir>\r
- </generator>\r
- </codeGenerators>\r
- <inspectDependencies>false</inspectDependencies>\r
- </configuration>\r
- </execution>\r
- </executions>\r
-\r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.yangtools</groupId>\r
- <artifactId>maven-sal-api-gen-plugin</artifactId>\r
- <version>${yang.codegen.version}</version>\r
- <type>jar</type>\r
- </dependency>\r
- </dependencies>\r
- </plugin>\r
- <plugin>\r
- <groupId>org.codehaus.mojo</groupId>\r
- <artifactId>build-helper-maven-plugin</artifactId>\r
- <version>1.7</version>\r
- <executions>\r
- <execution>\r
- <phase>generate-sources</phase>\r
- <goals>\r
- <goal>add-source</goal>\r
- </goals>\r
- <configuration>\r
- <sources>\r
- <source>target/generated-sources/sal</source>\r
- </sources>\r
- </configuration>\r
- </execution>\r
- </executions>\r
- </plugin>\r
- </plugins>\r
- <pluginManagement>\r
- <plugins>\r
- <!--This plugin's configuration is used to store Eclipse m2e settings\r
- only. It has no influence on the Maven build itself. -->\r
- <plugin>\r
- <groupId>org.eclipse.m2e</groupId>\r
- <artifactId>lifecycle-mapping</artifactId>\r
- <version>1.0.0</version>\r
- <configuration>\r
- <lifecycleMappingMetadata>\r
- <pluginExecutions>\r
- <pluginExecution>\r
- <pluginExecutionFilter>\r
- <groupId>org.opendaylight.yangtools</groupId>\r
- <artifactId>yang-maven-plugin</artifactId>\r
- <versionRange>[0.5,)</versionRange>\r
- <goals>\r
- <goal>generate-sources</goal>\r
- </goals>\r
- </pluginExecutionFilter>\r
- <action>\r
- <ignore></ignore>\r
- </action>\r
- </pluginExecution>\r
- </pluginExecutions>\r
- </lifecycleMappingMetadata>\r
- </configuration>\r
- </plugin>\r
- </plugins>\r
- </pluginManagement>\r
- </build>\r
- <pluginRepositories>\r
- <!-- OpenDayLight Repo Mirror -->\r
- <pluginRepository>\r
- <id>opendaylight-mirror</id>\r
- <name>opendaylight-mirror</name>\r
- <url>${nexusproxy}/groups/public/</url>\r
- <snapshots>\r
- <enabled>false</enabled>\r
- </snapshots>\r
- <releases>\r
- <enabled>true</enabled>\r
- <updatePolicy>never</updatePolicy>\r
- </releases>\r
- </pluginRepository>\r
- <!-- OpenDayLight Snapshot artifact -->\r
- <pluginRepository>\r
- <id>opendaylight-snapshot</id>\r
- <name>opendaylight-snapshot</name>\r
+ <nexus.repository.snapshot>opendaylight.release</nexus.repository.snapshot>
+ <yang.version>0.7.0-SNAPSHOT</yang.version>
+ <yang.codegen.version>0.7.0-SNAPSHOT</yang.codegen.version>
+ <bundle.plugin.version>2.3.7</bundle.plugin.version>
+ </properties>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${bundle.plugin.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <inherited>true</inherited>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.8.1</version>
+ <configuration>
+ <stylesheet>maven</stylesheet>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>aggregate</goal>
+ </goals>
+ <phase>site</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yang.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/generated-sources/sal</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>false</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yang.codegen.version}</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/generated-sources/sal</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings
+ only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <versionRange>[0.5,)</versionRange>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ <pluginRepositories>
+ <!-- OpenDayLight Repo Mirror -->
+ <pluginRepository>
+ <id>opendaylight-mirror</id>
+ <name>opendaylight-mirror</name>
+ <url>${nexusproxy}/groups/public/</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ </releases>
+ </pluginRepository>
+ <!-- OpenDayLight Snapshot artifact -->
+ <pluginRepository>
+ <id>opendaylight-snapshot</id>
+ <name>opendaylight-snapshot</name>
<url>${nexusproxy}/repositories/${nexus.repository.snapshot}/</url>
- <snapshots>\r
- <enabled>true</enabled>\r
- </snapshots>\r
- <releases>\r
- <enabled>false</enabled>\r
- </releases>\r
- </pluginRepository>\r
- </pluginRepositories>\r
-\r
- <repositories>\r
- <!-- OpenDayLight Repo Mirror -->\r
- <repository>\r
- <id>opendaylight-mirror</id>\r
- <name>opendaylight-mirror</name>\r
- <url>${nexusproxy}/groups/public/</url>\r
- <snapshots>\r
- <enabled>false</enabled>\r
- </snapshots>\r
- <releases>\r
- <enabled>true</enabled>\r
- <updatePolicy>never</updatePolicy>\r
- </releases>\r
- </repository>\r
- <!-- OpenDayLight Snapshot artifact -->\r
- <repository>\r
- <id>opendaylight-snapshot</id>\r
- <name>opendaylight-snapshot</name>\r
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <repositories>
+ <!-- OpenDayLight Repo Mirror -->
+ <repository>
+ <id>opendaylight-mirror</id>
+ <name>opendaylight-mirror</name>
+ <url>${nexusproxy}/groups/public/</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ </releases>
+ </repository>
+ <!-- OpenDayLight Snapshot artifact -->
+ <repository>
+ <id>opendaylight-snapshot</id>
+ <name>opendaylight-snapshot</name>
<url>${nexusproxy}/repositories/${nexus.repository.snapshot}/</url>
- <snapshots>\r
- <enabled>true</enabled>\r
- </snapshots>\r
- <releases>\r
- <enabled>false</enabled>\r
- </releases>\r
- </repository>\r
- </repositories>\r
-\r
- <distributionManagement>\r
- <!-- OpenDayLight Released artifact -->\r
- <repository>\r
- <id>opendaylight-release</id>\r
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </repository>
+ </repositories>
+
+ <distributionManagement>
+ <!-- OpenDayLight Released artifact -->
+ <repository>
+ <id>opendaylight-release</id>
<url>${nexusproxy}/repositories/${nexus.repository.release}/</url>
- </repository>\r
- <!-- OpenDayLight Snapshot artifact -->\r
- <snapshotRepository>\r
- <id>opendaylight-snapshot</id>\r
+ </repository>
+ <!-- OpenDayLight Snapshot artifact -->
+ <snapshotRepository>
+ <id>opendaylight-snapshot</id>
<url>${nexusproxy}/repositories/${nexus.repository.snapshot}/</url>
- </snapshotRepository>\r
- <!-- Site deployment -->\r
- <site>\r
- <id>website</id>\r
- <url>${sitedeploy}</url>\r
- </site>\r
- </distributionManagement>\r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.yangtools</groupId>\r
- <artifactId>yang-binding</artifactId>\r
- <version>${yang.codegen.version}</version>\r
- </dependency>\r
- </dependencies>\r
-</project>\r
+ </snapshotRepository>
+ <!-- Site deployment -->
+ <site>
+ <id>website</id>
+ <url>${sitedeploy}</url>
+ </site>
+ </distributionManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ <version>${yang.codegen.version}</version>
+ </dependency>
+ </dependencies>
+</project>
<sonar.jacoco.reportPath>target/code-coverage/jacoco.exec</sonar.jacoco.reportPath>
<sonar.jacoco.itReportPath>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.skippedModules>org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages</sonar.skippedModules>
- <sonar.profile>Sonar way with Findbugs</sonar.profile>
<spifly.version>1.0.0</spifly.version>
<spring-osgi.version>1.2.1</spring-osgi.version>
<spring-security-karaf.version>3.1.4.RELEASE</spring-security-karaf.version>
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;
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) {
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;
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)
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")
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);
}
}
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()) {
// 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
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;
// 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(
}};
}
+ @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<ReplicatedLogEntry> 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<Boolean>(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<ReplicatedLogEntry> 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<Boolean>(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
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;
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
public class MockRpcService implements RpcService {
- public Future<?> rpcnameWithInputNoOutput(DataObject input) {
+ public Future<?> rpcnameWithInputNoOutput(final DataObject input) {
return futureDataObj;
}
- public Future<RpcResult<DataObject>> rpcnameWithInputWithOutput(DataObject input) {
+ public Future<RpcResult<DataObject>> rpcnameWithInputWithOutput(final DataObject input) {
return futureDataObj;
}
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);
}
- private void validateForwardToDomBroker(ListenableFuture<RpcResult<?>> forwardToDomBroker,
- boolean expectedSuccess, DataObject expectedResult, int expectedErrorSize)
+ private void validateForwardToDomBroker(final ListenableFuture<RpcResult<?>> forwardToDomBroker,
+ final boolean expectedSuccess, final DataObject expectedResult, final int expectedErrorSize)
throws InterruptedException, ExecutionException {
assertNotNull(forwardToDomBroker);
assertEquals(expectedSuccess, forwardToDomBroker.get().isSuccessful());
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,
/*
* invokeOn Tests
*/
- private void setupRpcResultsWithOutput(int expectedErrorSize) {
+ private void setupRpcResultsWithOutput(final int expectedErrorSize) {
if (expectedErrorSize > 0) {
errors.add(rpcError);
}
when(mockMappingService.toDataDom(toDataDomInput)).thenReturn(outputInvokeOn);
}
- private void setupRpcResultsNoOutput(int expectedErrorSize) {
+ private void setupRpcResultsNoOutput(final int expectedErrorSize) {
if (expectedErrorSize > 0) {
errors.add(rpcError);
}
}
private void validateReturnedImmediateFuture(
- ListenableFuture<RpcResult<CompositeNode>> immediateFuture, boolean expectedSuccess,
- CompositeNode expectedReturn, int expectedErrorSize) throws InterruptedException,
+ final ListenableFuture<RpcResult<CompositeNode>> immediateFuture, final boolean expectedSuccess,
+ final CompositeNode expectedReturn, final int expectedErrorSize) throws InterruptedException,
ExecutionException {
assertNotNull(immediateFuture);
assertEquals(expectedSuccess, immediateFuture.get().isSuccessful());
--- /dev/null
+/*
+ * 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<PersistentMessages.CompositeModification> 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<GeneratedMessage.GeneratedExtension, PersistentMessages.CompositeModification> encode() {
+ Preconditions.checkState(byteString!=null);
+ Map<GeneratedMessage.GeneratedExtension, PersistentMessages.CompositeModification> 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
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);
}
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;
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.
*/
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<QName, String> attribute : ((AttributesContainer) data).getAttributes().entrySet()) {
writer.writeAttribute(attribute.getKey().getNamespace().toString(), attribute.getKey().getLocalName(), attribute.getValue());
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());
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;
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());
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);
}
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(),
}
+ 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();
--- /dev/null
+/*
+ * 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<ReplicatedLogEntry> 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());
+ }
+
+}
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;
ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(),
SCHEMA_CONTEXT))));
- int nListEntries = 11;
+ int nListEntries = 16;
Set<Integer> 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();
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));
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) {
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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<DOMNotificationListener> {
+
+}
--- /dev/null
+/*
+ * 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<Object> 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<? extends DeliveryStatus> 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<? extends Object> 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<? extends Object> 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<? extends Object> offerNotification(@Nonnull DOMNotification notification,
+ @Nonnegative long timeout, @Nonnull TimeUnit unit) throws InterruptedException;
+}
--- /dev/null
+/*
+ * 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<SchemaPath> 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);
+}
* 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 {
}
--- /dev/null
+/*
+ * 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<DOMNotificationListener> 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);
+ }
+}
--- /dev/null
+/*
+ * 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<? extends Object> putNotification(final DOMNotification notification) throws InterruptedException {
+ return delegate().putNotification(notification);
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification) {
+ return delegate().offerNotification(notification);
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification, final long timeout,
+ final TimeUnit unit) throws InterruptedException {
+ return delegate().offerNotification(notification, timeout, unit);
+ }
+}
--- /dev/null
+/*
+ * 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<SchemaPath> types) {
+ return delegate().registerNotificationListener(listener, types);
+ }
+
+ @Override
+ public DOMNotificationListenerRegistration registerNotificationListener(final DOMNotificationListener listener,
+ final SchemaPath... types) {
+ return delegate().registerNotificationListener(listener, types);
+ }
+}
}
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<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
+ final List<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
if (predicates == null) {
return null;
}
}
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<Predicate> toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) {
- List<Predicate> result = new ArrayList<>();
- List<String> predicates = new ArrayList<>();
- Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
+ final List<Predicate> result = new ArrayList<>();
+ final List<String> 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;
}
}
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);
private final List<IdentityValue> 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;
}
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);
}
private final String namespace;
private final String value;
- private final String prefix;
private List<Predicate> 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() {
return value;
}
- public String getPrefix() {
- return prefix;
- }
public List<Predicate> getPredicates() {
if (predicates == null) {
return Collections.unmodifiableList(predicates);
}
- public void setPredicates(List<Predicate> predicates) {
+ public void setPredicates(final List<Predicate> 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("]");
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;
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
if (name != null) {
sb.append(name.toString());
}
input == null ? "null" : input.getClass(), String.valueOf(input));
return null;
} else {
- TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+ final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
.from(type);
if (typeAwarecodec != null) {
if (input instanceof IdentityValuesDTO) {
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);
} else if (type instanceof InstanceIdentifierTypeDefinition) {
return instanceIdentifier.serialize(input);
} else {
- TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+ final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
.from(type);
if (typeAwarecodec != null) {
return typeAwarecodec.serialize(input);
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);
@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));
@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<Predicate> predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument)
+ final List<Predicate> predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument)
.getKeyValues());
identityValue.setPredicates(predicates);
} else if (pathArgument instanceof NodeWithValue && identityValue != null) {
- List<Predicate> predicates = new ArrayList<>();
- String value = String.valueOf(((NodeWithValue) pathArgument).getValue());
+ final List<Predicate> predicates = new ArrayList<>();
+ final String value = String.valueOf(((NodeWithValue) pathArgument).getValue());
predicates.add(new Predicate(null, value));
identityValue.setPredicates(predicates);
}
@Override
public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) {
- List<PathArgument> result = new ArrayList<PathArgument>();
- IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
- Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint);
+ final List<PathArgument> result = new ArrayList<PathArgument>();
+ 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());
}
DataNodeContainer parentContainer = module;
- List<IdentityValue> identities = data.getValuesWithNamespaces();
+ final List<IdentityValue> 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());
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 - {}",
}
pathArgument = new NodeWithValue(qName, leafListPredicate.getValue());
} else if (node instanceof ListSchemaNode) { // predicates are keys of list
- DataNodeContainer listNode = (DataNodeContainer) node;
- Map<QName, Object> predicatesMap = new HashMap<>();
- for (Predicate predicate : identityValue.getPredicates()) {
+ final DataNodeContainer listNode = (DataNodeContainer) node;
+ final Map<QName, Object> 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());
}
private List<Predicate> keyValuesToPredicateList(final Map<QName, Object> keyValues) {
- List<Predicate> result = new ArrayList<>();
- for (QName qName : keyValues.keySet()) {
- Object value = keyValues.get(qName);
+ final List<Predicate> 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;
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) {
}
@Override
- public void onDataChanged(AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+ public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> 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);
}
@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);
}
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()));
* DataChangeEvent
* @return Data in printable form.
*/
- private String prepareXmlFrom(AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
- Document doc = createDocument();
- Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
+ private String prepareXmlFrom(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> 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;
}
final DocumentBuilder bob;
try {
bob = DBF.newDocumentBuilder();
- } catch (ParserConfigurationException e) {
+ } catch (final ParserConfigurationException e) {
return null;
}
return bob.newDocument();
*/
private void addValuesToDataChangedNotificationEventElement(final Document doc,
final Element dataChangedNotificationEventElement,
- AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+ final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
addValuesFromDataToElement(doc, change.getCreatedData().keySet(), dataChangedNotificationEventElement,
Operation.CREATED);
if (change.getCreatedData().isEmpty()) {
* @param operation
* {@link Operation}
*/
- private void addValuesFromDataToElement(Document doc, Set<YangInstanceIdentifier> data, Element element,
- Operation operation) {
+ private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> 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);
}
}
* @param operation
* {@link Operation}
*/
- private void addValuesFromDataToElement(Document doc, Map<YangInstanceIdentifier, CompositeNode> data, Element element,
- Operation operation) {
+ private void addValuesFromDataToElement(final Document doc, final Map<YangInstanceIdentifier, CompositeNode> data, final Element element,
+ final Operation operation) {
if (data == null || data.isEmpty()) {
return;
}
- for (Entry<YangInstanceIdentifier, CompositeNode> entry : data.entrySet()) {
- Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), operation);
+ for (final Entry<YangInstanceIdentifier, CompositeNode> entry : data.entrySet()) {
+ final Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), operation);
element.appendChild(node);
}
}
* {@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);
// 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);
}
* @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.",
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;
}
*/
private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element) {
// Map< key = namespace, value = prefix>
- Map<String, String> prefixes = new HashMap<>();
- YangInstanceIdentifier instanceIdentifier = path;
- StringBuilder textContent = new StringBuilder();
+ final Map<String, String> 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<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
- for (QName keyValue : predicates.keySet()) {
- String predicateValue = String.valueOf(predicates.get(keyValue));
+ final Map<QName, Object> 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("='");
*/
private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent,
final QName qName, final Map<String, String> 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);
*/
private static String generateNewPrefix(final Collection<String> 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()));
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);
}
*/
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);
}
@Test
public void snAsYangIdentityrefWithQNamePrefixToXMLTest() {
serializeToXml(prepareIdentityrefData("prefix", true),
- "<lf11 xmlns:prefix=\"referenced:module\">prefix:iden</lf11>");
+ "<lf11 xmlns","=\"referenced:module\">",":iden</lf11>");
}
@Test
@Test
public void snAsYangInt8ToXmlTest() {
- String elName = "lfInt8";
+ final String elName = "lfInt8";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int8.getInstance()).deserialize("14"), elName),
"<" + elName + ">14</" + elName + ">");
@Test
public void snAsYangInt16ToXmlTest() {
- String elName = "lfInt16";
+ final String elName = "lfInt16";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int16.getInstance()).deserialize("3000"),
elName), "<" + elName + ">3000</" + elName + ">");
@Test
public void snAsYangInt32ToXmlTest() {
- String elName = "lfInt32";
+ final String elName = "lfInt32";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Int32.getInstance()).deserialize("201234"),
elName), "<" + elName + ">201234</" + elName + ">");
@Test
public void snAsYangInt64ToXmlTest() {
- String elName = "lfInt64";
+ final String elName = "lfInt64";
serializeToXml(
prepareCnStructForYangData(
TypeDefinitionAwareCodec.from(Int64.getInstance()).deserialize("5123456789"), elName), "<"
@Test
public void snAsYangUint8ToXmlTest() {
- String elName = "lfUint8";
+ final String elName = "lfUint8";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint8.getInstance()).deserialize("200"),
elName), "<" + elName + ">200</" + elName + ">");
@Test
public void snAsYangUint16ToXmlTest() {
- String elName = "lfUint16";
+ final String elName = "lfUint16";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint16.getInstance()).deserialize("4000"),
elName), "<" + elName + ">4000</" + elName + ">");
@Test
public void snAsYangUint32ToXmlTest() {
- String elName = "lfUint32";
+ final String elName = "lfUint32";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint32.getInstance())
.deserialize("4123456789"), elName), "<" + elName + ">4123456789</" + elName + ">");
@Test
public void snAsYangUint64ToXmlTest() {
- String elName = "lfUint64";
+ final String elName = "lfUint64";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(Uint64.getInstance())
.deserialize("5123456789"), elName), "<" + elName + ">5123456789</" + elName + ">");
@Test
public void snAsYangBinaryToXmlTest() {
- String elName = "lfBinary";
+ final String elName = "lfBinary";
serializeToXml(
prepareCnStructForYangData(
TypeDefinitionAwareCodec.from(BinaryType.getInstance()).deserialize(
@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<BitsTypeDefinition.Bit> bitList = Lists.newArrayList(mockBit1, mockBit2);
+ final List<BitsTypeDefinition.Bit> bitList = Lists.newArrayList(mockBit1, mockBit2);
- String elName = "lfBits";
+ final String elName = "lfBits";
serializeToXml(
prepareCnStructForYangData(
TypeDefinitionAwareCodec.from(BitsType.create(mock(SchemaPath.class), bitList)).deserialize(
@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<EnumPair> enumList = Lists.newArrayList(mockEnum);
+ final List<EnumPair> enumList = Lists.newArrayList(mockEnum);
- String elName = "lfEnumeration";
+ final String elName = "lfEnumeration";
serializeToXml(
prepareCnStructForYangData(
TypeDefinitionAwareCodec.from(
@Test
public void snAsYangEmptyToXmlTest() {
- String elName = "lfEmpty";
+ final String elName = "lfEmpty";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(EmptyType.getInstance()).deserialize(null),
elName), "<" + elName + "/>");
@Test
public void snAsYangBooleanToXmlTest() {
- String elName = "lfBoolean";
+ final String elName = "lfBoolean";
serializeToXml(
prepareCnStructForYangData(TypeDefinitionAwareCodec.from(BooleanType.getInstance()).deserialize("str"),
elName), "<" + elName + ">false</" + elName + ">");
@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<BitsTypeDefinition.Bit> bitList = Lists.newArrayList(mockBit1, mockBit2);
+ final List<BitsTypeDefinition.Bit> bitList = Lists.newArrayList(mockBit1, mockBit2);
- List<TypeDefinition<?>> types = Lists.<TypeDefinition<?>> newArrayList(Int8.getInstance(),
+ final List<TypeDefinition<?>> types = Lists.<TypeDefinition<?>> 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</" + elName + ">");
- String bits = "first second";
+ final String bits = "first second";
serializeToXml(prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bits), elName),
"<" + elName + ">first second</" + elName + ">");
- String bool = "str";
+ final String bool = "str";
serializeToXml(prepareCnStructForYangData(TypeDefinitionAwareCodec.from(unionType).deserialize(bool), elName),
"<" + elName + ">str</" + elName + ">");
}
assertNotNull(xmlString);
boolean containSearchedStr = false;
String strRepresentation = "";
- for (String searchedStr : xmlRepresentation) {
+ for (final String searchedStr : xmlRepresentation) {
if (xmlString.contains(searchedStr)) {
containSearchedStr = true;
break;
}
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);
} else {
value = "no qname value";
}
- MutableSimpleNode<Object> lf11 = NodeFactory.createMutableSimpleNode(
+ final MutableSimpleNode<Object> lf11 = NodeFactory.createMutableSimpleNode(
TestUtils.buildQName("lf11", "basic:module", "2013-12-2"), cont1, value, ModifyAction.CREATE, null);
cont1.getValue().add(lf11);
cont1.init();
}
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<Object> lf1 = NodeFactory.createMutableSimpleNode(
+ final MutableSimpleNode<Object> lf1 = NodeFactory.createMutableSimpleNode(
TestUtils.buildQName(leafName, "basic:module", "2013-12-2"), cont, data, ModifyAction.CREATE, null);
cont.getValue().add(lf1);
cont.init();
}
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<Object> lfBoolean = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfBoolean"),
+ final MutableSimpleNode<Object> lfBoolean = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfBoolean"),
cont, Boolean.TRUE, ModifyAction.CREATE, null);
- MutableSimpleNode<Object> lfLfref = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfLfref"), cont,
+ final MutableSimpleNode<Object> lfLfref = NodeFactory.createMutableSimpleNode(TestUtils.buildQName("lfLfref"), cont,
"true", ModifyAction.CREATE, null);
cont.getValue().add(lfBoolean);
cont.getValue().add(lfLfref);
private final static YangContextParser parser = new YangParserImpl();
- private static Set<Module> loadModules(String resourceDirectory) throws FileNotFoundException {
+ private static Set<Module> loadModules(final String resourceDirectory) throws FileNotFoundException {
final File testDir = new File(resourceDirectory);
final String[] fileList = testDir.list();
final List<File> testFiles = new ArrayList<File>();
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));
}
return parser.parseYangModels(testFiles);
}
- public static Set<Module> loadModulesFrom(String yangPath) {
+ public static Set<Module> 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<Module> modules) {
+ public static SchemaContext loadSchemaContext(final Set<Module> 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<Module> modules, String moduleName) {
- for (Module module : modules) {
+ public static Module findModule(final Set<Module> modules, final String moduleName) {
+ for (final Module module : modules) {
if (module.getName().equals(moduleName)) {
return module;
}
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);
}
}
- 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");
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;
}
* {@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<Module> modules, String schemaNodePath) {
- RestconfImpl restconf = RestconfImpl.getInstance();
+ public static void normalizeCompositeNode(final Node<?> node, final Set<Module> modules, final String schemaNodePath) {
+ final RestconfImpl restconf = RestconfImpl.getInstance();
ControllerContext.getInstance().setSchemas(TestUtils.loadSchemaContext(modules));
prepareMocksForRestconf(modules, restconf);
* module set has only one element then this element is returned.
*
*/
- public static Module resolveModule(String searchedModuleName, Set<Module> modules) {
+ public static Module resolveModule(final String searchedModuleName, final Set<Module> 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;
}
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;
}
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<Module> modules, RestconfImpl restconf) {
- ControllerContext controllerContext = ControllerContext.getInstance();
- BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
+ private static void prepareMocksForRestconf(final Set<Module> modules, final RestconfImpl restconf) {
+ final ControllerContext controllerContext = ControllerContext.getInstance();
+ final BrokerFacade mockedBrokerFacade = mock(BrokerFacade.class);
controllerContext.setSchemas(TestUtils.loadSchemaContext(modules));
restconf.setBroker(mockedBrokerFacade);
}
- public static Node<?> readInputToCnSn(String path, boolean dummyNamespaces,
- MessageBodyReader<Node<?>> reader) throws WebApplicationException {
+ public static Node<?> readInputToCnSn(final String path, final boolean dummyNamespaces,
+ final MessageBodyReader<Node<?>> 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);
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);
}
// return null;
// }
- public static Node<?> readInputToCnSn(String path, MessageBodyReader<Node<?>> reader) {
+ public static Node<?> readInputToCnSn(final String path, final MessageBodyReader<Node<?>> reader) {
return readInputToCnSn(path, false, reader);
}
- public static String writeCompNodeWithSchemaContextToOutput(Node<?> node, Set<Module> modules,
- DataSchemaNode dataSchemaNode, MessageBodyWriter<StructuredData> messageBodyWriter) throws IOException,
+ public static String writeCompNodeWithSchemaContextToOutput(final Node<?> node, final Set<Module> modules,
+ final DataSchemaNode dataSchemaNode, final MessageBodyWriter<StructuredData> 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));
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);
}
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*");
}
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<Node<?>> lst = new ArrayList<Node<?>>();
+ final List<Node<?>> lst = new ArrayList<Node<?>>();
lst.add(compositeNode);
if (schema instanceof ContainerSchemaNode) {
return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
"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<String, Object> keys) throws ParseException {
- Map<QName, Object> predicate = new HashMap<>();
- for (String key : keys.keySet()) {
+ public static YangInstanceIdentifier.NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName,
+ final String namespace, final String revision, final Map<String, Object> keys) throws ParseException {
+ final Map<QName, Object> predicate = new HashMap<>();
+ for (final String key : keys.keySet()) {
predicate.put(QName.create(namespace, revision, key), keys.get(key));
}
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<QName, Object> predicate = new HashMap<>();
+ final Map<QName, Object> predicate = new HashMap<>();
int i = 0;
while (i < keysAndValues.length) {
}
public static CompositeNode prepareCompositeNodeWithIetfInterfacesInterfacesData() {
- CompositeNodeBuilder<ImmutableCompositeNode> interfaceBuilder = ImmutableCompositeNode.builder();
+ final CompositeNodeBuilder<ImmutableCompositeNode> 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");
}
static NormalizedNode<?,?> prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException {
- String ietfInterfacesDate = "2013-07-04";
- String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces";
- DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntryNode = ImmutableMapEntryNodeBuilder.create();
+ final String ietfInterfacesDate = "2013-07-04";
+ final String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntryNode = ImmutableMapEntryNodeBuilder.create();
- Map<String, Object> predicates = new HashMap<>();
+ final Map<String, Object> predicates = new HashMap<>();
predicates.put("name", "eth0");
mapEntryNode.withNodeIdentifier(getNodeIdentifierPredicate("interface", namespace, ietfInterfacesDate,
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;
}
@Override
- protected void addExiHandlers(final NetconfEXICodec exiCodec) {
+ protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> 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
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;
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 {
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());
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;
private Date loginTime;
private long inRpcSuccess, inRpcFail, outRpcError;
- public NetconfServerSession(NetconfServerSessionListener sessionListener, Channel channel, long sessionId,
- NetconfHelloMessageAdditionalHeader header) {
+ public NetconfServerSession(final NetconfServerSessionListener sessionListener, final Channel channel, final long sessionId,
+ final NetconfHelloMessageAdditionalHeader header) {
super(sessionListener, channel, sessionId);
this.header = header;
LOG.debug("Session {} created", toString());
return builder.build();
}
- private Class<? extends Transport> getTransportForString(String transport) {
+ private Class<? extends Transport> getTransportForString(final String transport) {
switch(transport) {
case "ssh" :
return NetconfSsh.class;
}
}
- private String formatDateTime(Date loginTime) {
+ private String formatDateTime(final Date loginTime) {
SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_DATE_FORMAT);
return dateFormat.format(loginTime);
}
}
@Override
- protected void addExiHandlers(NetconfEXICodec exiCodec) {
- replaceMessageDecoder(new NetconfEXIToMessageDecoder(exiCodec));
- replaceMessageEncoderAfterNextMessage(new NetconfMessageToEXIEncoder(exiCodec));
+ protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {
+ replaceMessageDecoder(decoder);
+ replaceMessageEncoderAfterNextMessage(encoder);
}
@Override
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;
private final Set<NetconfOperationService> services;
private final String netconfSessionIdForReporting;
- public NetconfOperationServiceSnapshotImpl(Set<NetconfOperationServiceFactory> factories, String sessionIdForReporting) {
- Set<NetconfOperationService> services = new HashSet<>();
+ public NetconfOperationServiceSnapshotImpl(final Set<NetconfOperationServiceFactory> factories, final String sessionIdForReporting) {
+ final Builder<NetconfOperationService> 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;
<artifactId>netconf-ssh</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-testtool</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-ssh</artifactId>
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;
@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));
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<ListenableFuture<RpcResult<NetconfMessage>>> futures = Lists.newArrayList();
}
}
- 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));
return b.build();
}
- @Mock
- private RemoteDevice<NetconfSessionCapabilities, NetconfMessage> mockedRemoteDevice;
-
- private NetconfDeviceCommunicator getSessionListener() {
- MockitoAnnotations.initMocks(this);
+ static NetconfDeviceCommunicator getSessionListener() {
+ RemoteDevice<NetconfSessionCapabilities, NetconfMessage> 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);
return mockAuth;
}
- public AuthenticationHandler getAuthHandler() throws IOException {
+ public static AuthenticationHandler getAuthHandler() throws IOException {
return new LoginPassword(USERNAME, PASSWORD);
}
--- /dev/null
+/*
+ * 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<Future<?>> tasks = new LinkedList<Future<?>>();
+
+ 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<Integer> 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<Future<?>> 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<ListenableFuture<RpcResult<NetconfMessage>>> 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<RpcResult<NetconfMessage>> 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<ListenableFuture<RpcResult<NetconfMessage>>> futures;
+ private AtomicInteger responseCounter;
+
+ public NetconfITSecureTestToolRunnable(NetconfMessage getConfig, int it, NetconfDeviceCommunicator sessionListener, List<ListenableFuture<RpcResult<NetconfMessage>>> futures, AtomicInteger responseCounter){
+ this.getConfig = getConfig;
+ this.it = it;
+ this.sessionListener = sessionListener;
+ this.futures = futures;
+ this.responseCounter = responseCounter;
+ }
+
+ @Override
+ public void run(){
+
+ ListenableFuture<RpcResult<NetconfMessage>> netconfMessageFuture;
+
+ netconfMessageFuture = sessionListener.sendRequest(getConfig, QName.create("namespace", "2012-12-12", "get"));
+
+ futures.add(netconfMessageFuture);
+ Futures.addCallback(netconfMessageFuture, new FutureCallback<RpcResult<NetconfMessage>>() {
+
+ @Override
+ public void onSuccess(final RpcResult<NetconfMessage> 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)));
+
+ }
+ }
+ );
+ }
+ }
+
+}
</encoder>
</appender>
- <logger name="org.opendaylight.controller.netconf" level="TRACE"/>
- <logger name="org.opendaylight.controller.sal.connect.netconf" level="TRACE"/>
+ <logger name="org.opendaylight.controller.netconf" level="INFO"/>
+ <logger name="org.opendaylight.controller.sal.connect.netconf" level="DEBUG"/>
<root level="error">
<appender-ref ref="STDOUT" />
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;
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;
LOG.warn("Unable to parse EXI parameters from {} on session {}", startExiMessage, this, e);
throw new IllegalArgumentException("Cannot parse options", e);
}
+
final NetconfEXICodec exiCodec = new NetconfEXICodec(exiParams.getOptions());
- addExiHandlers(exiCodec);
+ final NetconfMessageToEXIEncoder exiEncoder;
+ try {
+ exiEncoder = NetconfMessageToEXIEncoder.create(exiCodec);
+ } catch (EXIOptionsException | TransmogrifierException e) {
+ LOG.warn("Failed to instantiate EXI encoder for {} on session {}", exiCodec, this, e);
+ throw new IllegalStateException("Cannot instantiate encoder for options", e);
+ }
+
+ final NetconfEXIToMessageDecoder exiDecoder;
+ try {
+ exiDecoder = NetconfEXIToMessageDecoder.create(exiCodec);
+ } catch (EXIOptionsException e) {
+ LOG.warn("Failed to instantiate EXI decodeer for {} on session {}", exiCodec, this, e);
+ throw new IllegalStateException("Cannot instantiate encoder for options", e);
+ }
+
+ addExiHandlers(exiDecoder, exiEncoder);
LOG.debug("Session {} EXI handlers added to pipeline", this);
}
- protected abstract void addExiHandlers(NetconfEXICodec exiCodec);
+ /**
+ * Add a set encoder/decoder tuple into the channel pipeline as appropriate.
+ *
+ * @param decoder EXI decoder
+ * @param encoder EXI encoder
+ */
+ protected abstract void addExiHandlers(ByteToMessageDecoder decoder, MessageToByteEncoder<NetconfMessage> encoder);
public final boolean isUp() {
return up;
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;
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 {
/**
* 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<Short, GrammarCache> GRAMMAR_CACHES = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<Short, GrammarCache>() {
+ @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);
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;
}
}
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
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()));
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<NetconfMessage> {
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();
}
}
}
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));
}
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
// 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;
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);
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;
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;
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
}
@Override
- protected void addExiHandlers(final NetconfEXICodec exiCodec) {}
+ protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {}
@Override
public void stopExiCommunication() {}
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;
@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);
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
- static class Params {
+ public static class Params {
@Arg(dest = "schemas-dir")
public File schemasDir;
-<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+<rpc message-id="101" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<candidate/>
<version>1.1.0-SNAPSHOT</version>
<relativePath>opendaylight/commons/parent</relativePath>
</parent>
+
<artifactId>releasepom</artifactId>
<version>0.2.0-SNAPSHOT</version>
<packaging>pom</packaging>
+ <name>controller</name> <!-- Used by Sonar to set project name -->
+
<prerequisites>
<maven>3.0</maven>
</prerequisites>
+
<modules>
<!-- md-sal -->