ModuleJMXRegistrator newModuleJMXRegistrator = baseJMXRegistrator
.createModuleJMXRegistrator();
+ OsgiRegistration osgiRegistration = null;
if (entry.hasOldModule()) {
ModuleInternalInfo oldInternalInfo = entry.getOldInternalInfo();
DynamicReadableWrapper oldReadableConfigBean = oldInternalInfo
currentConfig.remove(entry.getName());
// test if old instance == new instance
- if (oldReadableConfigBean.getInstance().equals(
- module.getInstance())) {
+ if (oldReadableConfigBean.getInstance().equals(module.getInstance())) {
// reused old instance:
// wrap in readable dynamic mbean
reusedInstances.add(primaryReadOnlyON);
+ osgiRegistration = oldInternalInfo.getOsgiRegistration();
} else {
// recreated instance:
// it is responsibility of module to call the old instance -
// we just need to unregister configbean
recreatedInstances.add(primaryReadOnlyON);
+
+ // close old osgi registration
+ oldInternalInfo.getOsgiRegistration().close();
}
- // close old osgi registration in any case
- oldInternalInfo.getOsgiRegistration().close();
+
// close old module jmx registrator
oldInternalInfo.getModuleJMXRegistrator().close();
} else {
}
// register to OSGi
- OsgiRegistration osgiRegistration = beanToOsgiServiceManager
- .registerToOsgi(module.getClass(),
- newReadableConfigBean.getInstance(),
- entry.getName());
+ if (osgiRegistration == null) {
+ osgiRegistration = beanToOsgiServiceManager.registerToOsgi(module.getClass(),
+ newReadableConfigBean.getInstance(), entry.getName());
+ }
RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = runtimeRegistrators
.get(entry.getName());
this.transactionModuleJMXRegistration = transactionModuleJMXRegistration;
}
+
+ /**
+ * Use {@link #getIdentifier()} instead.
+ */
+ @Deprecated
public ModuleIdentifier getName() {
return name;
}
maybeOldInternalInfo.getOrderingIdx());
}
- @Deprecated
+
public Module getModule() {
return module;
}
return null;
}
- private void blankTransaction() {
+ private synchronized void blankTransaction() {
// create transaction
ObjectName tx = configRegistry.beginConfig();
CommitStatus commitStatus = configRegistry.commitConfig(tx);
protected ConfigRegistryJMXClient configRegistryClient;
protected BaseJMXRegistrator baseJmxRegistrator;
protected InternalJMXRegistrator internalJmxRegistrator;
+ protected BundleContext mockedContext;
+ protected ServiceRegistration<?> mockedServiceRegistration;
// this method should be called in @Before
protected void initConfigTransactionManagerImpl(
configRegistryJMXRegistrator = new ConfigRegistryJMXRegistrator(
platformMBeanServer);
- BundleContext context = mock(BundleContext.class);
- ServiceRegistration<?> mockedServiceRegistration = mock(ServiceRegistration.class);
+ this.mockedContext = mock(BundleContext.class);
+ this.mockedServiceRegistration = mock(ServiceRegistration.class);
doNothing().when(mockedServiceRegistration).unregister();
- doReturn(mockedServiceRegistration).when(context).registerService(
+ doReturn(mockedServiceRegistration).when(mockedContext).registerService(
Matchers.any(String[].class), any(Closeable.class),
any(Dictionary.class));
internalJmxRegistrator = new InternalJMXRegistrator(platformMBeanServer);
baseJmxRegistrator = new BaseJMXRegistrator(internalJmxRegistrator);
- configRegistry = new ConfigRegistryImpl(resolver, context,
+ configRegistry = new ConfigRegistryImpl(resolver, mockedContext,
platformMBeanServer, baseJmxRegistrator);
try {
configRegistryJMXRegistrator.registerToJMX(configRegistry);
--- /dev/null
+<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">
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-subsystem</artifactId>
+ <version>0.2.3-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netty-config-api</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-transport</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.opendaylight.controller.config.api.*,
+ io.netty.channel,
+ io.netty.util,
+ io.netty.util.concurrent
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.controller.config.yang.netty
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module netty {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netty";
+ prefix "netty";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ organization "Cisco Systems, Inc.";
+
+ contact "Milos Fabian <milfabia@cisco.com>";
+
+ description
+ "This module contains the base YANG definitions for
+ netty services.
+
+ Copyright (c)2013 Cisco Systems, Inc. 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";
+
+ revision "2013-11-19" {
+ description
+ "Initial revision.";
+ }
+
+ identity netty-threadgroup {
+ description
+ "Configuration wrapper around netty's threadgroup";
+
+ base "config:service-type";
+ config:java-class "io.netty.channel.EventLoopGroup";
+ }
+
+ identity netty-event-executor {
+ description
+ "Configuration wrapper around netty's event executor";
+
+ base "config:service-type";
+ config:java-class "io.netty.util.concurrent.EventExecutor";
+ }
+
+ identity netty-timer {
+ description
+ "Configuration wrapper around netty's timer";
+
+ base "config:service-type";
+ config:java-class "io.netty.util.Timer";
+ }
+}
\ No newline at end of file
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>threadpool-config-api</artifactId>
+ <artifactId>netty-config-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
<!--test dependencies -->
<dependency>
</Export-Package>
<Import-Package>
com.google.common.base,
- org.opendaylight.controller.config.yang.threadpool,
+ org.opendaylight.controller.config.yang.netty,
io.netty.util.concurrent,
org.opendaylight.controller.config.api,
org.opendaylight.controller.config.api.annotations,
@Override
public void validate() {
super.validate();
- // Add custom validation for module attributes here.
}
@Override
module netty-event-executor {
yang-version 1;
namespace "urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor";
- prefix "netty-t";
+ prefix "netty-ee";
import config { prefix config; revision-date 2013-04-05; }
- import threadpool { prefix th; revision-date 2013-04-09; }
+ import netty { prefix netty; revision-date 2013-11-19; }
organization "Cisco Systems, Inc.";
contact "Milos Fabian <milfabia@cisco.com>";
description
- "This module contains the base YANG definitions for NS-OS
- thread-related services.
+ "This module contains the base YANG definitions for
+ netty event executor implementation.
Copyright (c)2013 Cisco Systems, Inc. All rights reserved.;
identity netty-global-event-executor {
base config:module-type;
- config:provided-service th:netty-event-executor;
+ config:provided-service netty:netty-event-executor;
config:java-name-prefix GlobalEventExecutor;
}
}
}
-
-
}
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>threadpool-config-api</artifactId>
+ <artifactId>netty-config-api</artifactId>
<version>${project.version}</version>
</dependency>
-
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
-
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
-
<!--test dependencies -->
<dependency>
<groupId>junit</groupId>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.7</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
com.google.common.base,
io.netty.channel.nio,
- org.opendaylight.controller.config.yang.threadpool,
+ org.opendaylight.controller.config.yang.netty,
io.netty.util.concurrent,
org.opendaylight.controller.config.api,
org.opendaylight.controller.config.api.annotations,
*/
package org.opendaylight.controller.config.yang.netty.threadgroup;
-import com.google.common.base.Preconditions;
import io.netty.channel.nio.NioEventLoopGroup;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+
/**
*
*/
@Override
public void validate(){
if(getThreadCount()!=null) {
- Preconditions.checkArgument(getThreadCount() > 0, "Thread count cannot be < 0");
+ JmxAttributeValidationException.checkCondition(getThreadCount() > 0, "value must be greater than 0",
+ threadCountJmxAttribute);
}
}
// vi: set smarttab et sw=4 tabstop=4:
-module nsos-threadpool {
+module threadgroup {
yang-version 1;
namespace "urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup";
- prefix "netty-t";
+ prefix "netty-th";
import config { prefix config; revision-date 2013-04-05; }
- import threadpool { prefix th; revision-date 2013-04-09; }
+ import netty { prefix netty; revision-date 2013-11-19; }
organization "Cisco Systems, Inc.";
contact "Robert Varga <rovarga@cisco.com>";
description
- "This module contains the base YANG definitions for NS-OS
- thread-related services.
+ "This module contains the base YANG definitions for
+ netty threadgroup implementation.
Copyright (c)2013 Cisco Systems, Inc. All rights reserved.";
identity netty-threadgroup-fixed {
base config:module-type;
- config:provided-service th:netty-threadgroup;
+ config:provided-service netty:netty-threadgroup;
config:java-name-prefix NettyThreadgroup;
}
--- /dev/null
+<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">
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-subsystem</artifactId>
+ <version>0.2.3-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netty-timer-config</artifactId>
+ <description>Configuration Wrapper around netty's timer</description>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+ <prerequisites>
+ <maven>3.0.4</maven>
+ </prerequisites>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-config-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <!--test dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-manager</artifactId>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-manager</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ </Export-Package>
+ <Import-Package>
+ javax.management,
+ com.google.common.base,
+ org.opendaylight.controller.config.yang.netty,
+ org.opendaylight.controller.config.yang.threadpool,
+ io.netty.util,
+ org.opendaylight.controller.config.api,
+ org.opendaylight.controller.config.api.annotations,
+ org.opendaylight.controller.config.api.runtime,
+ org.opendaylight.controller.config.spi,
+ org.slf4j,
+ org.osgi.framework
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <distributionManagement>
+ <site>
+ <id>${project.artifactId}</id>
+ <name>NETTY-TIMER-CONFIG Module site</name>
+ <url>${basedir}/target/site/${project.artifactId}</url>
+ </site>
+ </distributionManagement>
+</project>
\ No newline at end of file
--- /dev/null
+/**
+ * Generated file
+
+ * Generated from: yang module name: netty-event-executor yang module local name: netty-hashed-wheel-timer
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Tue Nov 19 12:49:59 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.netty.timer;
+
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+
+/**
+*
+*/
+public final class HashedWheelTimerModule extends
+ org.opendaylight.controller.config.yang.netty.timer.AbstractHashedWheelTimerModule {
+
+ public HashedWheelTimerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public HashedWheelTimerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ HashedWheelTimerModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void validate() {
+ super.validate();
+ if (getTickDuration() != null) {
+ JmxAttributeValidationException.checkCondition(getTickDuration() > 0, "value must be greater than 0",
+ tickDurationJmxAttribute);
+ }
+ if (getTicksPerWheel() != null) {
+ JmxAttributeValidationException.checkCondition(getTicksPerWheel() > 0, "value must be greater than 0",
+ ticksPerWheelJmxAttribute);
+ }
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ TimeUnit unit = TimeUnit.MILLISECONDS;
+ if (getTickDuration() != null && getThreadFactoryDependency() == null && getTicksPerWheel() == null) {
+ return new HashedWheelTimerCloseable(new HashedWheelTimer(getTickDuration(), unit));
+ }
+ if (getTickDuration() != null && getThreadFactoryDependency() == null && getTicksPerWheel() != null) {
+ return new HashedWheelTimerCloseable(new HashedWheelTimer(getTickDuration(), unit, getTicksPerWheel()));
+ }
+ if (getTickDuration() == null && getThreadFactoryDependency() != null && getTicksPerWheel() == null) {
+ return new HashedWheelTimerCloseable(new HashedWheelTimer(getThreadFactoryDependency()));
+ }
+ if (getTickDuration() != null && getThreadFactoryDependency() != null && getTicksPerWheel() == null) {
+ return new HashedWheelTimerCloseable(new HashedWheelTimer(getThreadFactoryDependency(), getTickDuration(),
+ unit));
+ }
+ if (getTickDuration() != null && getThreadFactoryDependency() != null && getTicksPerWheel() != null) {
+ return new HashedWheelTimerCloseable(new HashedWheelTimer(getThreadFactoryDependency(), getTickDuration(),
+ unit, getTicksPerWheel()));
+ }
+ return new HashedWheelTimerCloseable(new HashedWheelTimer());
+ }
+
+ static final private class HashedWheelTimerCloseable implements AutoCloseable, Timer {
+
+ private final Timer timer;
+
+ public HashedWheelTimerCloseable(Timer timer) {
+ this.timer = timer;
+ }
+
+ @Override
+ public void close() throws Exception {
+ stop();
+ }
+
+ @Override
+ public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
+ return this.timer.newTimeout(task, delay, unit);
+ }
+
+ @Override
+ public Set<Timeout> stop() {
+ return this.timer.stop();
+ }
+
+ }
+}
--- /dev/null
+/**
+ * Generated file
+
+ * Generated from: yang module name: netty-event-executor yang module local name: netty-hashed-wheel-timer
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Tue Nov 19 12:49:59 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.netty.timer;
+
+/**
+*
+*/
+public class HashedWheelTimerModuleFactory extends
+ org.opendaylight.controller.config.yang.netty.timer.AbstractHashedWheelTimerModuleFactory {
+
+}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module netty-timer {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netty:timer";
+ prefix "netty-timer";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import netty { prefix netty; revision-date 2013-11-19; }
+ import threadpool { prefix th; revision-date 2013-04-09; }
+
+ organization "Cisco Systems, Inc.";
+
+ contact "Milos Fabian <milfabia@cisco.com>";
+
+ description
+ "This module contains the base YANG definitions for
+ netty timer implementation.
+
+ Copyright (c)2013 Cisco Systems, Inc. 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";
+
+ revision "2013-11-19" {
+ description
+ "Initial revision";
+ }
+
+ identity netty-hashed-wheel-timer {
+ base config:module-type;
+ config:provided-service netty:netty-timer;
+ config:java-name-prefix HashedWheelTimer;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netty-hashed-wheel-timer {
+ when "/config:modules/config:module/config:type = 'netty-hashed-wheel-timer'";
+
+ leaf tick-duration {
+ type uint32;
+ }
+
+ leaf ticks-per-wheel {
+ type uint16;
+ }
+
+ container thread-factory {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity th:threadfactory;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.config.yang.netty.timer;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFactoryModuleFactory;
+import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFactoryModuleMXBean;
+
+public class HashedWheelTimerModuleTest extends AbstractConfigTest {
+
+ private HashedWheelTimerModuleFactory factory;
+ private NamingThreadFactoryModuleFactory threadFactory;
+ private final String instanceName = "hashed-wheel-timer1";
+
+ @Before
+ public void setUp() {
+ factory = new HashedWheelTimerModuleFactory();
+ threadFactory = new NamingThreadFactoryModuleFactory();
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(factory, threadFactory));
+ }
+
+ public void testValidationExceptionTickDuration() throws InstanceAlreadyExistsException {
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ try {
+ createInstance(transaction, instanceName, 0L, 10, true);
+ transaction.validateConfig();
+ Assert.fail();
+ } catch (ValidationException e) {
+ Assert.assertTrue(e.getMessage().contains("TickDuration value must be greater than 0"));
+ }
+ }
+
+ public void testValidationExceptionTicksPerWheel() throws InstanceAlreadyExistsException {
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ try {
+ createInstance(transaction, instanceName, 500L, 0, true);
+ transaction.validateConfig();
+ Assert.fail();
+ } catch (ValidationException e) {
+ Assert.assertTrue(e.getMessage().contains("TicksPerWheel value must be greater than 0"));
+ }
+ }
+
+ @Test
+ public void testCreateBean() throws InstanceAlreadyExistsException, ValidationException,
+ ConflictingVersionException {
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+
+ createInstance(transaction, instanceName, 500L, 10, true);
+ createInstance(transaction, instanceName + 1, null, null, false);
+ createInstance(transaction, instanceName + 2, 500L, 10, false);
+ createInstance(transaction, instanceName + 3, 500L, null, false);
+ transaction.validateConfig();
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(4, factory.getImplementationName());
+ assertStatus(status, 5, 0, 0);
+ }
+
+ @Test
+ public void testReusingOldInstance() throws InstanceAlreadyExistsException, ConflictingVersionException,
+ ValidationException {
+
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, instanceName, 500L, 10, true);
+
+ transaction.commit();
+
+ transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, factory.getImplementationName());
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(1, factory.getImplementationName());
+ assertStatus(status, 0, 0, 2);
+ }
+
+ @Test
+ public void testReconfigure() throws InstanceAlreadyExistsException, ConflictingVersionException,
+ ValidationException, InstanceNotFoundException {
+
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, instanceName, 500L, 10, true);
+ transaction.commit();
+
+ transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, factory.getImplementationName());
+ HashedWheelTimerModuleMXBean mxBean = transaction.newMBeanProxy(
+ transaction.lookupConfigBean(factory.getImplementationName(), instanceName),
+ HashedWheelTimerModuleMXBean.class);
+ mxBean.setTicksPerWheel(20);
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(1, factory.getImplementationName());
+ assertStatus(status, 0, 1, 1);
+ }
+
+ private ObjectName createInstance(ConfigTransactionJMXClient transaction, String instanceName,
+ final Long tickDuration, final Integer ticksPerWheel, final boolean hasThreadfactory)
+ throws InstanceAlreadyExistsException {
+ ObjectName nameCreated = transaction.createModule(factory.getImplementationName(), instanceName);
+ HashedWheelTimerModuleMXBean mxBean = transaction
+ .newMBeanProxy(nameCreated, HashedWheelTimerModuleMXBean.class);
+ mxBean.setTickDuration(tickDuration);
+ mxBean.setTicksPerWheel(ticksPerWheel);
+ if (hasThreadfactory) {
+ mxBean.setThreadFactory(createThreadfactoryInstance(transaction, "thread-factory1", "th"));
+ }
+ return nameCreated;
+ }
+
+ private ObjectName createThreadfactoryInstance(ConfigTransactionJMXClient transaction, String instanceName,
+ final String namePrefix) throws InstanceAlreadyExistsException {
+ ObjectName nameCreated = transaction.createModule(threadFactory.getImplementationName(), instanceName);
+ NamingThreadFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+ NamingThreadFactoryModuleMXBean.class);
+ mxBean.setNamePrefix(namePrefix);
+ return nameCreated;
+ }
+
+}
<module>yang-test</module>
<module>logback-config</module>
<module>threadpool-config-api</module>
+ <module>netty-config-api</module>
<module>threadpool-config-impl</module>
<module>netty-threadgroup-config</module>
<module>netty-event-executor-config</module>
+ <module>netty-timer-config</module>
</modules>
<profiles>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-transport</artifactId>
- </dependency>
</dependencies>
<build>
<Import-Package>
org.opendaylight.controller.config.api.*,
com.google.common.eventbus,
- io.netty.channel,
- io.netty.util.concurrent
</Import-Package>
<Export-Package>
org.opendaylight.controller.config.threadpool,
base "threadpool";
config:java-class "org.opendaylight.controller.config.threadpool.ScheduledThreadPool";
}
-
-
- identity netty-threadgroup {
- description
- "Configuration wrapper around netty's threadgroup";
-
- base "config:service-type";
- config:java-class "io.netty.channel.EventLoopGroup";
- }
-
- identity netty-event-executor {
- description
- "Configuration wrapper around netty's event executor";
-
- base "config:service-type";
- config:java-class "io.netty.util.concurrent.EventExecutor";
- }
-
-
}
@Override
public final boolean isModuleImplementingServiceInterface(Class<? extends ${abstractServiceInterfaceType}> serviceInterface) {
- return serviceIfcs.contains(serviceInterface);
+ for (Class<?> ifc: serviceIfcs) {
+ if (serviceInterface.isAssignableFrom(ifc)){
+ return true;
+ }
+ }
+ return false;
}
@Override
// available if source level is 5.0
if (c.getID() == 1610613329)
continue;
+ if (c.getID() == 1610613328) // 'for each' statements are only available if source level is 5.0
+ continue;
fail("Error in generated source code " + file + ":"
- + c.getSourceLineNumber() + " " + c.toString());
+ + c.getSourceLineNumber() + " id: " + c.getID() + " message:" + c.toString());
}
ASTVisitor visitor = verifiers.get(file.getName());
<dependencies>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
+ <groupId>${project.groupId}</groupId>
<artifactId>yang-jmx-generator</artifactId>
</dependency>
</dependencies>
</Import-Package>
<Export-Package>
org.opendaylight.controller.config.yang.store.api,
- org.opendaylight.controller.config.yang.store.spi
</Export-Package>
</instructions>
</configuration>
*/
package org.opendaylight.controller.config.yang.store.api;
-import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
-
/**
* Yang store OSGi service
*/
*/
YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException;
-
- /**
- * Allows for registering for change notifications.
- */
- YangStoreListenerRegistration registerListener(YangStoreListener listener);
-
}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.yang.store.spi;
-
-import java.net.URL;
-import java.util.Collection;
-
-/**
- * Implementation of this interface gets notified when bundle containing yang files in META-INF/yang has been
- * added or removed. One notification is sent per one bundle.
- */
-public interface YangStoreListener {
-
- void onAddedYangURL(Collection<URL> url);
-
- void onRemovedYangURL(Collection<URL> url);
-
-}
<artifactId>mockito-configuration</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ </dependency>
</dependencies>
<build>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Bundle-Activator>org.opendaylight.controller.config.yang.store.impl.YangStoreActivator
- </Bundle-Activator>
- <Private-Package>
- org.opendaylight.controller.config.yang.store.impl,
- </Private-Package>
-
+ <Bundle-Activator>org.opendaylight.controller.config.yang.store.impl.YangStoreActivator</Bundle-Activator>
<Import-Package>
org.opendaylight.controller.config.yang.store.api,
- org.opendaylight.controller.config.yang.store.spi,
org.opendaylight.controller.config.yangjmxgenerator,
com.google.common.base,
com.google.common.collect,
org.opendaylight.yangtools.yang.common,
org.opendaylight.yangtools.yang.model.api,
org.opendaylight.yangtools.sal.binding.generator.spi,
- org.opendaylight.yangtools.yang.parser.impl
+ org.opendaylight.yangtools.yang.parser.impl,
</Import-Package>
<Export-Package>
</Export-Package>
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.api.YangStoreListenerRegistration;
import org.opendaylight.controller.config.yang.store.api.YangStoreService;
import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
-import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
-import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.BundleTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.concurrent.GuardedBy;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Note on consistency:
* is not preserved. We thus maintain two maps, one containing consistent snapshot, other inconsistent. The
* container should eventually send all events and thus making the inconsistent map redundant.
*/
-public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Object>, YangStoreService {
+public class ExtenderYangTracker extends BundleTracker<Object> implements YangStoreService, AutoCloseable {
- private static final Logger logger = LoggerFactory
- .getLogger(ExtenderYangTrackerCustomizer.class);
+ private static final Logger logger = LoggerFactory.getLogger(ExtenderYangTracker.class);
private final Multimap<Bundle, URL> consistentBundlesToYangURLs = HashMultimap.create();
private final YangStoreCache cache = new YangStoreCache();
private final MbeParser mbeParser;
- private final List<YangStoreListener> listeners = new ArrayList<>();
- public ExtenderYangTrackerCustomizer() {
- this(new MbeParser());
+ public ExtenderYangTracker(Optional<Pattern> maybeBlacklist, BundleContext bundleContext) {
+ this(new MbeParser(), maybeBlacklist, bundleContext);
}
+ @GuardedBy("this")
+ private Optional<Pattern> maybeBlacklist;
+
@VisibleForTesting
- ExtenderYangTrackerCustomizer(MbeParser mbeParser) {
+ ExtenderYangTracker(MbeParser mbeParser, Optional<Pattern> maybeBlacklist, BundleContext bundleContext) {
+ super(bundleContext, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, null);
this.mbeParser = mbeParser;
+ this.maybeBlacklist = maybeBlacklist;
+ open();
}
@Override
- public Object addingBundle(Bundle bundle, BundleEvent event) {
+ public synchronized Object addingBundle(Bundle bundle, BundleEvent event) {
// Ignore system bundle:
// system bundle might have config-api on classpath &&
if (bundle.getBundleId() == 0)
return bundle;
+ if (maybeBlacklist.isPresent()) {
+ Matcher m = maybeBlacklist.get().matcher(bundle.getSymbolicName());
+ if (m.matches()) {
+ logger.debug("Ignoring {} because it is in blacklist {}", bundle, maybeBlacklist);
+ return bundle;
+ }
+ }
+
Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
if (enumeration != null && enumeration.hasMoreElements()) {
synchronized (this) {
Multimap<Bundle, URL> proposedNewState = HashMultimap.create(consistentBundlesToYangURLs);
proposedNewState.putAll(inconsistentBundlesToYangURLs);
proposedNewState.putAll(bundle, addedURLs);
- boolean adding = true;
- if (tryToUpdateState(addedURLs, proposedNewState, adding) == false) {
+
+ Preconditions.checkArgument(addedURLs.size() > 0, "No change can occur when no URLs are changed");
+ boolean success;
+ String failureReason = null;
+ try(YangStoreSnapshotImpl snapshot = createSnapshot(mbeParser, proposedNewState)) {
+ updateCache(snapshot);
+ success = true;
+ } catch(YangStoreException e) {
+ failureReason = e.toString();
+ success = false;
+ }
+ if (success){
+ // consistent state
+ // merge into
+ consistentBundlesToYangURLs.clear();
+ consistentBundlesToYangURLs.putAll(proposedNewState);
+ inconsistentBundlesToYangURLs.clear();
+
+ logger.info("Yang store updated to new consistent state containing {} yang files", consistentBundlesToYangURLs.size());
+ logger.trace("Yang store updated to new consistent state containing {}", consistentBundlesToYangURLs);
+ } else {
+ // inconsistent state
+ logger.debug("Yang store is falling back on last consistent state containing {}, inconsistent yang files {}, reason {}",
+ consistentBundlesToYangURLs, inconsistentBundlesToYangURLs, failureReason);
+ logger.warn("Yang store is falling back on last consistent state containing {} files, inconsistent yang files size is {}, reason {}",
+ consistentBundlesToYangURLs.size(), inconsistentBundlesToYangURLs.size(), failureReason);
inconsistentBundlesToYangURLs.putAll(bundle, addedURLs);
}
}
return bundle;
}
- private synchronized boolean tryToUpdateState(Collection<URL> changedURLs, Multimap<Bundle, URL> proposedNewState, boolean adding) {
- Preconditions.checkArgument(changedURLs.size() > 0, "No change can occur when no URLs are changed");
- try(YangStoreSnapshot snapshot = createSnapshot(mbeParser, proposedNewState)) {
- // consistent state
- // merge into
- consistentBundlesToYangURLs.clear();
- consistentBundlesToYangURLs.putAll(proposedNewState);
- inconsistentBundlesToYangURLs.clear();
- // update cache
- updateCache(snapshot);
- logger.info("Yang store updated to new consistent state");
- logger.trace("Yang store updated to new consistent state containing {}", consistentBundlesToYangURLs);
-
- notifyListeners(changedURLs, adding);
- return true;
- } catch(YangStoreException e) {
- // inconsistent state
- logger.debug("Yang store is falling back on last consistent state containing {}, inconsistent yang files {}, reason {}",
- consistentBundlesToYangURLs, inconsistentBundlesToYangURLs, e.toString());
- return false;
- }
- }
-
- private void updateCache(YangStoreSnapshot snapshot) {
+ private void updateCache(YangStoreSnapshotImpl snapshot) {
cache.cacheYangStore(consistentBundlesToYangURLs, snapshot);
}
logger.debug("Modified bundle {} {} {}", bundle, event, object);
}
- /**
- * Notifiers get only notified when consistent snapshot has changed.
- */
- private void notifyListeners(Collection<URL> changedURLs, boolean adding) {
- Preconditions.checkArgument(changedURLs.size() > 0, "Cannot notify when no URLs changed");
- if (changedURLs.size() > 0) {
- RuntimeException potential = new RuntimeException("Error while notifying listeners");
- for (YangStoreListener listener : listeners) {
- try {
- if (adding) {
- listener.onAddedYangURL(changedURLs);
- } else {
- listener.onRemovedYangURL(changedURLs);
- }
- } catch(RuntimeException e) {
- potential.addSuppressed(e);
- }
- }
- if (potential.getSuppressed().length > 0) {
- throw potential;
- }
- }
- }
-
-
/**
* If removing YANG files makes yang store inconsistent, method {@link #getYangStoreSnapshot()}
* will throw exception. There is no rollback.
@Override
public synchronized void removedBundle(Bundle bundle, BundleEvent event, Object object) {
inconsistentBundlesToYangURLs.removeAll(bundle);
- Collection<URL> consistentURLsToBeRemoved = consistentBundlesToYangURLs.removeAll(bundle);
-
- if (consistentURLsToBeRemoved.isEmpty()){
- return; // no change
- }
- boolean adding = false;
- notifyListeners(consistentURLsToBeRemoved, adding);
+ consistentBundlesToYangURLs.removeAll(bundle);
}
@Override
public synchronized YangStoreSnapshot getYangStoreSnapshot()
throws YangStoreException {
- Optional<YangStoreSnapshot> yangStoreOpt = cache.getCachedYangStore(consistentBundlesToYangURLs);
+ Optional<YangStoreSnapshot> yangStoreOpt = cache.getSnapshotIfPossible(consistentBundlesToYangURLs);
if (yangStoreOpt.isPresent()) {
logger.trace("Returning cached yang store {}", yangStoreOpt.get());
return yangStoreOpt.get();
}
- YangStoreSnapshot snapshot = createSnapshot(mbeParser, consistentBundlesToYangURLs);
+ YangStoreSnapshotImpl snapshot = createSnapshot(mbeParser, consistentBundlesToYangURLs);
updateCache(snapshot);
return snapshot;
}
- private static YangStoreSnapshot createSnapshot(MbeParser mbeParser, Multimap<Bundle, URL> multimap) throws YangStoreException {
+ private static YangStoreSnapshotImpl createSnapshot(MbeParser mbeParser, Multimap<Bundle, URL> multimap) throws YangStoreException {
try {
- YangStoreSnapshot yangStoreSnapshot = mbeParser.parseYangFiles(fromUrlsToInputStreams(multimap));
+ YangStoreSnapshotImpl yangStoreSnapshot = mbeParser.parseYangFiles(fromUrlsToInputStreams(multimap));
logger.trace("{} module entries parsed successfully from {} yang files",
yangStoreSnapshot.countModuleMXBeanEntries(), multimap.values().size());
return yangStoreSnapshot;
});
}
- @Override
- public synchronized YangStoreListenerRegistration registerListener(final YangStoreListener listener) {
- listeners.add(listener);
- return new YangStoreListenerRegistration() {
- @Override
- public void close() {
- listeners.remove(listener);
- }
- };
+ public synchronized void setMaybeBlacklist(Optional<Pattern> maybeBlacklistPattern) {
+ maybeBlacklist = maybeBlacklistPattern;
+ cache.invalidate();
}
+}
- private static final class YangStoreCache {
-
- Set<URL> cachedUrls;
- YangStoreSnapshot cachedYangStoreSnapshot;
-
- Optional<YangStoreSnapshot> getCachedYangStore(
- Multimap<Bundle, URL> bundlesToYangURLs) {
- Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
- if (cachedUrls != null && cachedUrls.equals(urls)) {
- Preconditions.checkState(cachedYangStoreSnapshot != null);
- return Optional.of(cachedYangStoreSnapshot);
- }
- return Optional.absent();
+class YangStoreCache {
+ @GuardedBy("this")
+ private Set<URL> cachedUrls = Collections.emptySet();
+ @GuardedBy("this")
+ private Optional<YangStoreSnapshotImpl> cachedYangStoreSnapshot = Optional.absent();
+
+ synchronized Optional<YangStoreSnapshot> getSnapshotIfPossible(Multimap<Bundle, URL> bundlesToYangURLs) {
+ Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
+ if (cachedUrls != null && cachedUrls.equals(urls)) {
+ Preconditions.checkState(cachedYangStoreSnapshot.isPresent());
+ YangStoreSnapshot freshSnapshot = new YangStoreSnapshotImpl(cachedYangStoreSnapshot.get());
+ return Optional.of(freshSnapshot);
}
+ return Optional.absent();
+ }
- private static Set<URL> setFromMultimapValues(
- Multimap<Bundle, URL> bundlesToYangURLs) {
- Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
- Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
- return urls;
- }
+ private static Set<URL> setFromMultimapValues(
+ Multimap<Bundle, URL> bundlesToYangURLs) {
+ Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
+ Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
+ return urls;
+ }
- void cacheYangStore(Multimap<Bundle, URL> urls,
- YangStoreSnapshot yangStoreSnapshot) {
- this.cachedUrls = setFromMultimapValues(urls);
- this.cachedYangStoreSnapshot = yangStoreSnapshot;
- }
+ synchronized void cacheYangStore(Multimap<Bundle, URL> urls,
+ YangStoreSnapshotImpl yangStoreSnapshot) {
+ this.cachedUrls = setFromMultimapValues(urls);
+ this.cachedYangStoreSnapshot = Optional.of(yangStoreSnapshot);
+ }
+ synchronized void invalidate() {
+ cachedUrls.clear();
+ if (cachedYangStoreSnapshot.isPresent()){
+ cachedYangStoreSnapshot.get().close();
+ cachedYangStoreSnapshot = Optional.absent();
+ }
}
}
*/
package org.opendaylight.controller.config.yang.store.impl;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
public class MbeParser {
- public YangStoreSnapshot parseYangFiles(
+ public YangStoreSnapshotImpl parseYangFiles(
Collection<? extends InputStream> allInput)
throws YangStoreException {
YangParserImpl parser = new YangParserImpl();
*/
package org.opendaylight.controller.config.yang.store.impl;
-import java.util.Dictionary;
-import java.util.Hashtable;
-
+import com.google.common.base.Optional;
import org.opendaylight.controller.config.yang.store.api.YangStoreService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.util.tracker.BundleTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class YangStoreActivator implements BundleActivator {
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.regex.Pattern;
- private BundleTracker bundleTracker;
- private ServiceRegistration<YangStoreService> registration;
- private static final Logger logger = LoggerFactory
- .getLogger(YangStoreActivator.class);
+public class YangStoreActivator implements BundleActivator {
+ private static final Logger logger = LoggerFactory.getLogger(YangStoreActivator.class);
@Override
public void start(BundleContext context) throws Exception {
- ExtenderYangTrackerCustomizer customizerAndService = new ExtenderYangTrackerCustomizer();
- bundleTracker = new BundleTracker(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, customizerAndService);
- bundleTracker.open();
-
+ // get blacklist
+ Optional<Pattern> maybeBlacklistPattern = Optional.absent();
+ String blacklist = context.getProperty("yangstore.blacklist");
+ if (blacklist != null) {
+ try {
+ maybeBlacklistPattern = Optional.of(Pattern.compile(blacklist));
+ } catch (RuntimeException e) {
+ logger.error("Cannot parse blacklist regex " + blacklist, e);
+ throw e;
+ }
+ }
+ ExtenderYangTracker extenderYangTracker = new ExtenderYangTracker(maybeBlacklistPattern, context);
Dictionary<String, ?> properties = new Hashtable<>();
- registration = context.registerService(YangStoreService.class,
- customizerAndService, properties);
+ context.registerService(YangStoreService.class, extenderYangTracker, properties);
}
@Override
public void stop(BundleContext context) throws Exception {
- try {
- bundleTracker.close();
- } catch (Exception e) {
- logger.warn("Exception while closing bundleTracker", e);
- }
- if (registration != null) {
- try {
- registration.unregister();
- } catch (Exception e) {
- logger.warn("Exception while unregistring yang store service",
- e);
- }
- }
+
}
}
*/
package org.opendaylight.controller.config.yang.store.impl;
-import java.util.Map;
-import java.util.Map.Entry;
-
import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.yangtools.yang.model.api.Module;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+
public class YangStoreSnapshotImpl implements YangStoreSnapshot {
- private final Map<String /* Namespace from yang file */, Map<String /*
- * Name
- * of
- * module
- * entry
- * from
- * yang
- * file
- */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
+ private final Map<String /* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
private final Map<String, Entry<Module, String>> moduleMap;
public YangStoreSnapshotImpl(
Map<String, Map<String, ModuleMXBeanEntry>> moduleMXBeanEntryMap,
Map<String, Entry<Module, String>> moduleMap) {
- this.moduleMXBeanEntryMap = moduleMXBeanEntryMap;
- this.moduleMap = moduleMap;
+ this.moduleMXBeanEntryMap = Collections.unmodifiableMap(moduleMXBeanEntryMap);
+ this.moduleMap = Collections.unmodifiableMap(moduleMap);
+ }
+
+ public YangStoreSnapshotImpl(YangStoreSnapshotImpl yangStoreSnapshot) {
+ this.moduleMXBeanEntryMap = yangStoreSnapshot.moduleMXBeanEntryMap;
+ this.moduleMap = yangStoreSnapshot.moduleMap;
}
@Override
*/
package org.opendaylight.controller.config.yang.store.impl;
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.yang.store.api.YangStoreException;
import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleListener;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
+import java.util.regex.Pattern;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollectionOf;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
public class ExtenderYangTrackerCustomizerTest {
- private ExtenderYangTrackerCustomizer tested;
+ private ExtenderYangTracker tested;
@Mock
private MbeParser parser;
@Mock
- private YangStoreSnapshot yangStoreSnapshot;
+ private YangStoreSnapshotImpl yangStoreSnapshot;
+ @Mock
+ private BundleContext bundleContext;
@Before
public void setUp() throws YangStoreException {
MockitoAnnotations.initMocks(this);
-
- tested = new ExtenderYangTrackerCustomizer(parser);
+ doNothing().when(bundleContext).addBundleListener(any(BundleListener.class));
+ doReturn(new Bundle[0]).when(bundleContext).getBundles();
+ tested = new ExtenderYangTracker(parser, Optional.<Pattern>absent(), bundleContext);
doReturn(yangStoreSnapshot).when(parser).parseYangFiles(
anyCollectionOf(InputStream.class));
doReturn(22).when(yangStoreSnapshot).countModuleMXBeanEntries();
doReturn("mock yang store").when(yangStoreSnapshot).toString();
doNothing().when(yangStoreSnapshot).close();
+ doReturn(Collections.emptyMap()).when(yangStoreSnapshot).getModuleMap();
}
@Test
bundle = getMockedBundle(10, false);
tested.addingBundle(bundle, null);
- for(int i = 0; i< 10; i++){
+ for(int i = 0; i< 20; i++){
tested.getYangStoreSnapshot();
}
- verify(parser, times(5)).parseYangFiles(
- anyCollectionOf(InputStream.class));
+ verify(parser, times(7)).parseYangFiles(anyCollectionOf(InputStream.class));
returnedStore = tested.getYangStoreSnapshot();
verifyNoMoreInteractions(parser);
- assertEquals(yangStoreSnapshot, returnedStore);
}
int bundleCounter = 1;
doReturn(1L).when(mock).getBundleId();
doReturn("mockedBundle").when(mock).toString();
+ doReturn("mockedBundle").when(mock).getSymbolicName();
return mock;
}
*/
package org.opendaylight.controller.config.yang.store.impl;
-import static org.junit.Assert.assertNotNull;
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
-import org.apache.commons.io.IOUtils;
-import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.api.YangStoreListenerRegistration;
-import org.opendaylight.controller.config.yang.store.api.YangStoreService;
-import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
-import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
+import static org.junit.Assert.assertNotNull;
public class HardcodedYangStoreService implements YangStoreService {
}
return new MbeParser().parseYangFiles(byteArrayInputStreams);
}
-
- @Override
- public YangStoreListenerRegistration registerListener(YangStoreListener listener){
- throw new UnsupportedOperationException("Cannot register for changes on this service");
- }
}
<artifactId>inventory-manager</artifactId>
<version>${mdsal.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>forwardingrules-manager</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<version>1.2.0</version>
</dependency>
+ <!-- threadpool -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-config-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>threadpool-config-impl</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-threadgroup-config</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-event-executor-config</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netty-timer-config</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+
+
<!-- toaster example I'm pretty sure we should trim -->
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
reference\:file\:../lib/jersey-server-1.17.jar@2:start
# Netconf startup configuration
-netconf.tcp.address=127.0.0.1
+netconf.tcp.address=0.0.0.0
netconf.tcp.port=8383
#netconf.tls.address=127.0.0.1
#netconf.tls.keystore.password=
netconf.config.persister.storageAdapterClass=org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter
+yangstore.blacklist=.*controller.model.*
# Set Default start level for framework
osgi.bundles.defaultStartLevel=4
<logger name="org.opendaylight.controller.sal.implementation" level="INFO"/>
<logger name="org.opendaylight.controller.sal.implementation.internal.Inventory" level="INFO"/>
<logger name="org.opendaylight.controller.sal.implementation.internal.Topology" level="INFO"/>
+ <!-- zeromq router and zeromq routing table -->
+ <logger name="org.opendaylight.controller.sal.connector.remoterpc" level="INFO" />
<!-- Functional Modules -->
<logger name="org.opendaylight.controller.arphandler" level="INFO"/>
<logger name="org.opendaylight.controller.hosttracker" level="INFO"/>
+++ /dev/null
-<?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>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.opendaylight</artifactId>
- <version>1.4.1-SNAPSHOT</version>
- <relativePath>../../commons/opendaylight</relativePath>
- </parent>
- <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>
- <tag>HEAD</tag>
- </scm>
-
- <artifactId>forwardingrulesmanager_mdsal</artifactId>
- <version>1.0-SNAPSHOT</version>
- <packaging>bundle</packaging>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>${bundle.plugin.version}</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Embed-Transitive>
- false
- </Embed-Transitive>
- <Bundle-Activator>
- org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl.FRMConsumerImpl
- </Bundle-Activator>
- </instructions>
- <manifestLocation>${project.basedir}/META-INF</manifestLocation>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-api</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>flow-management-compatibility</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-service</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-management</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-</project>
}
override readFlow(FlowKey key) {
- val flowCfg = manager.getStaticFlow(key.id,key.node.toADNode());
+ val flowCfg = manager.getStaticFlow(String.valueOf(key.id), key.node.toADNode());
return flowCfg.toConfigurationFlow;
}
}
idleTimeout = source.idleTimeout
match = source.match
node = source.node
- key = new FlowKey(sourceCfg.name,node);
+ key = new FlowKey(Long.parseLong(sourceCfg.name),node);
return it.build();
}
static def toFlowConfig(Flow sourceCfg) {
val flow = toFlow(sourceCfg);
val it = new FlowConfig;
- name = sourceCfg.key.id
+ name = String.valueOf(sourceCfg.id);
node = sourceCfg.node.toADNode();
return it
static def toFlowConfig(InstanceIdentifier<?> identifier) {
val it = new FlowConfig()
val FlowKey key = ((identifier.path.get(2) as IdentifiableItem<Flow,FlowKey>).key)
- name = key.id;
+ name = String.valueOf(key.id);
node = key.node.toADNode();
return it;
Flow createSampleFlow(String name, NodeRef node) {
FlowBuilder ret = new FlowBuilder();
- FlowKey key = new FlowKey(name, node);
+ FlowKey key = new FlowKey(Long.parseLong(name), node);
ret.setKey(key);
return ret.build();
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification
import org.opendaylight.yangtools.yang.common.RpcResult
import org.slf4j.LoggerFactory
// NOOP : Not supported by AD SAL
}
+ override onNodeErrorNotification(NodeErrorNotification notification) {
+ // NOOP : Not supported by AD SAL
+ }
+
+ override onNodeExperimenterErrorNotification(
+ NodeExperimenterErrorNotification notification) {
+ // NOOP : Not supported by AD SAL
+ }
}
--- /dev/null
+<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>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.opendaylight.controller.md</groupId>
+ <artifactId>forwardingrules-manager</artifactId>
+ <packaging>bundle</packaging>
+
+ <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:MD-SAL</url>
+ </scm>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.forwardingrulesmanager.consumer.impl.FRMConsumerImpl</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>xtend-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>flow-management-compatibility</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-flow-management</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.opendaylight.controller.clustering.services.IClusterContainerServices;
-package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
import java.util.ArrayList;
import java.util.List;
-package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
import java.util.ArrayList;
import java.util.EnumSet;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeFlow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
private IContainer container;
private static final String NAMEREGEX = "^[a-zA-Z0-9]+$";
private static ConcurrentMap<Integer, Flow> staticFlows;
- private static ConcurrentMap<Integer, Integer> staticFlowsOrdinal;
+ private static ConcurrentMap<Integer, Integer> staticFlowsOrdinal = new ConcurrentHashMap<Integer, Integer>();
/*
* Inactive flow list. This is for the global instance of FRM It will
* contain all the flow entries which were installed on the global container
@Override
public void onSwitchFlowRemoved(SwitchFlowRemoved notification) {
// TODO
+ }
+
+ @Override
+ public void onNodeErrorNotification(NodeErrorNotification notification) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onNodeExperimenterErrorNotification(
+ NodeExperimenterErrorNotification notification) {
+ // TODO Auto-generated method stub
+
};
}
-package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
import java.util.ArrayList;
import java.util.Collection;
-package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
import java.util.List;
-package org.opendaylight.controller.forwardingrulesmanager_mdsal.consumer.impl;
+package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
import java.util.ArrayList;
import java.util.Collection;
import ietf-inet-types {prefix inet; revision-date "2010-09-24";}
import ietf-yang-types {prefix yang; revision-date "2010-09-24";}
import opendaylight-l2-types {prefix l2t; revision-date "2013-08-27";}
- import opendaylight-match-types {prefix match; revision-date 2013-10-26";}
+ import opendaylight-match-types {prefix match; revision-date "2013-10-26";}
revision "2013-11-12" {
description "Initial revision of action service";
bit SEND_FLOW_REM;
}
}
+
+ typedef removed_reason_flags {
+ type bits {
+ bit IDLE_TIMEOUT;
+ bit HARD_TIMEOUT;
+ bit DELETE;
+ bit GROUP_DELETE;
+ }
+ }
grouping generic_flow_attributes {
leaf priority {
grouping flow-mod-removed {
uses generic_flow_attributes;
+ leaf removed_reason {
+ type removed_reason_flags;
+ }
+
leaf duration_nsec {
type uint32;
}
leaf duration_sec {
type uint32;
}
-
- leaf idle_timeout {
- type uint16;
- }
-
- leaf hard_timeout {
- type uint16;
- }
-
+
leaf packet_count {
type uint64;
}
-
+
leaf byte_count {
type uint64;
}
+
container match {
uses match:match;
}
}
}
- grouping group-statistics-request {
- list group-stats {
- key "group-id";
-
- leaf group-id {
- type int32;
- }
- }
- }
-
grouping group-statistics {
leaf group-id {
}
}
+ grouping group-features {
+ uses group-types;
+
+ leaf capabilities {
+ type enumeration {
+ enum select-weight;
+ enum select-liveness;
+ enum chaining;
+ enum chaining-checks;
+ }
+ }
+
+ leaf-list max-groups {
+ type uint32;
+ description "Maximum number of groups for each type";
+ max-elements 4;
+ }
+
+ leaf-list actions {
+ type uint32;
+ description "Bitmap number OFPAT_* that are supported";
+ max-elements 4;
+ }
+ }
+
+ grouping group-statistics-request {
+ list group-stats {
+ key "group-id";
+
+ leaf group-id {
+ type int32;
+ }
+ }
+ }
+
+
grouping group-statistics-reply {
+
list group-stats {
key "group-stats-order";
leaf group-stats-order {
}
}
- grouping group-desc-stats {
+ grouping group-desc-stats-reply {
+
list group-desc-stats {
key "order-id";
-
leaf order-id {
type int32;
}
}
}
- grouping group-features {
- list group-features {
- key "order";
- leaf order {
- type int32;
- }
-
- uses group-types;
-
- leaf capabilities {
- type enumeration {
- enum select-weight;
- enum select-liveness;
- enum chaining;
- enum chaining-checks;
- }
- }
-
- leaf-list max-groups {
- type uint32;
- description "Maximum number of groups for each type";
- max-elements 4;
- }
-
- leaf-list actions {
- type uint32;
- description "Bitmap number OFPAT_* that are supported";
- max-elements 4;
- }
- }
- }
+ grouping group-features-reply {
+ uses group-features;
+ }
+
}
\ No newline at end of file
}
}
- grouping meter-stats-config-request {
- list meter-stats {
- key "meter-id";
-
- leaf meter-id {
- type int32;
- }
- }
- }
-
grouping meter-statistics {
leaf meter-id {
}
}
+ grouping meter-features {
+
+ leaf max_meter {
+ type yang:counter32;
+ }
+
+ leaf band_types {
+ type yang:counter32;
+ }
+
+ leaf capabilities {
+ type yang:counter32;
+ }
+
+ leaf max_bands {
+ type uint8;
+ }
+
+ leaf max_color {
+ type uint8;
+ }
+ }
+
+ grouping meter-stats-config-request {
+ list meter-stats {
+ key "meter-id";
+
+ leaf meter-id {
+ type int32;
+ }
+ }
+ }
+
grouping meter-statistics-reply {
list meter-stats {
key "meter-stats-order";
}
}
- grouping meter-config-stats {
+ grouping meter-config-stats-reply {
list meter-config-stats {
key "meter-config-order";
-
leaf meter-config-order {
type int32;
}
}
}
- grouping meter-features {
- list meter-features {
- key "meter-feature-order";
-
- leaf meter-feature-order {
- type yang:counter32;
- }
-
- leaf max_meter {
- type yang:counter32;
- }
-
- leaf band_types {
- type yang:counter32;
- }
-
- leaf capabilities {
- type yang:counter32;
- }
-
- leaf max_bands {
- type uint8;
- }
-
- leaf max_color {
- type uint8;
- }
- }
- }
+ grouping meter-features-reply {
+ uses meter-features;
+ }
+
}
\ No newline at end of file
revision "2013-09-25" {
description "Initial revision of Port Inventory model";
}
-
+
+ typedef port-reason {
+ type enumeration {
+ enum add;
+ enum delete;
+ enum update;
+ }
+ }
typedef port-config {
type bits {
enum live;
}
}
-
typedef port-features {
type bits {
}
}
+ grouping flow-port-status {
+ leaf reason {
+ type port-reason;
+ }
+
+ uses flow-capable-port;
+ }
+
grouping flow-capable-port {
uses common-port;
}
}
- grouping ofp-port-mod {
+ grouping port-mod {
container port {
list port {
key "port-mod-order";
key "node id";
leaf id {
- type string;
+ type uint32;
}
uses flow:flow-entry;
}
key "id node";
leaf id {
- type string;
+ type uint32;
}
uses group-entry;
key "id node";
leaf id {
- type string;
+ type uint32;
}
uses meter-entry;
leaf node {
type inv:node-ref;
}
- uses port:ofp-port-mod;
+ uses port:port-mod;
}
container ports {
key "id node";
leaf id {
- type string;
+ type uint32;
}
uses port-entry;
key "id node";
leaf id {
- type string;
+ type uint32;
}
uses table-entry;
--- /dev/null
+module flow-errors {
+ namespace "urn:opendaylight:flow:errors";
+ prefix error;
+
+ revision "2013-11-16" {
+ description "Initial revision of error";
+ }
+
+ typedef error-type {
+ type enumeration {
+ enum hello-failed;
+ enum bad-request;
+ enum bad-action;
+ enum bad-instruction;
+ enum bad-match;
+ enum flow-mod-failed;
+ enum group-mod-failed;
+ enum port-mod-failed;
+ enum table-mod-failed;
+ enum queue-op-failed;
+ enum switch-config-failed;
+ enum role-request-failed;
+ enum meter-mod-failed;
+ enum table-features-failed;
+ enum experimenter {
+ value "65535";
+ }
+ }
+ }
+
+ grouping error-message {
+ leaf type {
+ type error-type;
+ }
+
+ leaf code {
+ type uint16;
+ }
+
+ leaf data {
+ type string;
+ }
+ }
+
+ grouping experimenter-error-message {
+ leaf type {
+ type error-type;
+ }
+
+ leaf exp-type {
+ type uint16;
+ }
+
+ leaf experimenter-id {
+ type uint32;
+ }
+
+ leaf data {
+ type string;
+ }
+ }
+}
\ No newline at end of file
import ietf-inet-types {prefix inet; revision-date "2010-09-24";}
import opendaylight-flow-types {prefix types;revision-date "2013-10-26";}
import flow-capable-transaction {prefix tr;}
+ import flow-errors {prefix error;}
revision "2013-08-19" {
description "Initial revision of flow service";
notification switch-flow-removed {
uses node-flow-removed;
}
+
+ notification node-error-notification {
+ uses error:error-message;
+ uses tr:transaction-aware;
+ }
+
+ notification node-experimenter-error-notification {
+ uses error:experimenter-error-message;
+ uses tr:transaction-aware;
+ }
}
\ No newline at end of file
grouping node-port {
uses "inv:node-context-ref";
- uses port-type:ofp-port-mod;
+ uses port-type:flow-port-status;
}
/** Base configuration structure **/
uses "inv:node-context-ref";
container original-port {
- uses port-type:ofp-port-mod;
+ uses port-type:port-mod;
}
container updated-port {
- uses port-type:ofp-port-mod;
+ uses port-type:port-mod;
}
}
import opendaylight-group-types {prefix group-types;revision-date "2013-10-18";}
import flow-capable-transaction {prefix tr;}
+ contact
+ "Anilkumar Vishnoi
+ Email: avishnoi@in.ibm.com";
+
revision "2013-11-11" {
description "Initial revision of group statistics service";
}
- typedef group-stats-ref {
- type instance-identifier;
- }
-
- grouping group-stats-response {
- uses "inv:node-context-ref";
-
- list group-statistics{
- uses group-types:group-statistics;
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "node-group-statistics";
+ container group-statistics {
+ //config "false";
+ uses group-types:group-statistics-reply;
}
}
- typedef group-features-ref {
- type instance-identifier;
- }
-
- grouping group-features-response {
- uses "inv:node-context-ref";
-
- uses group-types:group-features;
- }
-
- typedef group-desc-ref {
- type instance-identifier;
- }
-
- grouping group-desc-response {
- uses "inv:node-context-ref";
-
- list group-desc-stats {
- uses group-types:group-desc-stats;
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "node-group-desc-stats";
+ container group-desc {
+ //config "false";
+ uses group-types:group-desc-stats-reply;
}
}
-
- container group-all-statistics {
- container group-stats {
- uses group-stats-response;
- }
-
- container group-features {
- uses group-features-response;
- }
-
- container group-desc {
- uses group-desc-response;
- }
+
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "node-group-features";
+ container group-features {
+ //config "false";
+ uses group-types:group-features-reply;
+ }
}
// RPC calls
rpc get-all-group-statistics {
input {
- uses inv:node-context-ref;
+ uses inv:node;
}
output {
- uses group-stats-response;
+ uses group-types:group-statistics-reply;
uses tr:transaction-aware;
}
rpc get-group-statistics {
input {
- uses inv:node-context-ref;
+ uses inv:node;
leaf group-id{
type group-types:group-id;
}
+
}
output {
- uses group-stats-response;
+ uses group-types:group-statistics-reply;
uses tr:transaction-aware;
}
rpc get-group-description {
input {
- uses inv:node-context-ref;
+ uses inv:node;
}
output {
- uses group-desc-response;
+ uses group-types:group-desc-stats-reply;
uses tr:transaction-aware;
}
}
rpc get-group-features {
input {
- uses inv:node-context-ref;
+ uses inv:node;
}
output {
- uses group-features-response;
+ uses group-types:group-features-reply;
uses tr:transaction-aware;
}
}
//Notification calls
notification group-statistics-updated {
- leaf group-stats-id {
- type group-stats-ref;
- }
-
- uses group-stats-response;
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses group-types:group-statistics-reply;
uses tr:transaction-aware;
}
notification group-desc-stats-updated {
- leaf group-desc-id {
- type group-desc-ref;
- }
-
- uses group-desc-response;
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses group-types:group-desc-stats-reply;
uses tr:transaction-aware;
}
notification group-features-updated {
- leaf group-features-id {
- type group-features-ref;
- }
-
- uses group-features-response;
+ leaf moreReplies {
+ type boolean;
+ }
+ uses inv:node;
+ uses group-types:group-features-reply;
uses tr:transaction-aware;
}
}
import opendaylight-meter-types {prefix meter-types;revision-date "2013-09-18";}
import flow-capable-transaction {prefix tr;}
+ contact
+ "Anilkumar Vishnoi
+ Email: avishnoi@in.ibm.com";
revision "2013-11-11" {
description "Initial revision of meter statistics service";
}
- typedef meter-stats-ref {
- type instance-identifier;
- }
-
- grouping meter-stats-response {
- uses "inv:node-context-ref";
-
- list meter-statistics {
- uses meter-types:meter-statistics;
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "node-meter-statistics";
+ container meter-statistics {
+ //config "false";
+ uses meter-types:meter-statistics-reply;
}
}
- typedef meter-config-ref {
- type instance-identifier;
- }
-
- grouping meter-config-response {
- uses "inv:node-context-ref";
-
- list meter-config-stats {
- uses meter-types:meter-config-stats;
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "node-meter-config-stats";
+ container meter-config-stats {
+ //config "false";
+ uses meter-types:meter-config-stats-reply;
}
}
-
- typedef meter-features-ref {
- type instance-identifier;
- }
-
- grouping meter-features-response {
- uses "inv:node-context-ref";
-
- uses meter-types:meter-features;
+
+ augment "/inv:nodes/inv:node" {
+ ext:augment-identifier "node-meter-features";
+ container meter-features {
+ //config "false";
+ uses meter-types:meter-features-reply;
+ }
}
- container meter-all-stats {
- container meter-stats {
- uses meter-stats-response;
- }
-
- container meter-config {
- uses meter-config-response;
- }
-
- container meter-features {
- uses meter-features-response;
- }
- }
// RPC calls
rpc get-all-meter-statistics {
input {
- uses inv:node-context-ref;
+ uses inv:node;
}
output {
- uses meter-stats-response;
+ uses meter-types:meter-statistics-reply;
uses tr:transaction-aware;
}
rpc get-meter-statistics {
input {
- uses inv:node-context-ref;
+ uses inv:node;
leaf meter-id{
type meter-types:meter-id;
}
}
output {
- uses meter-stats-response;
+ uses meter-types:meter-statistics-reply;
uses tr:transaction-aware;
}
rpc get-all-meter-config-statistics {
input {
- uses inv:node-context-ref;
+ uses inv:node;
}
output {
- uses meter-config-response;
+ uses meter-types:meter-config-stats-reply;
uses tr:transaction-aware;
}
}
rpc get-meter-features {
input {
- uses inv:node-context-ref;
+ uses inv:node;
}
output {
- uses meter-features-response;
+ uses meter-types:meter-features-reply;
uses tr:transaction-aware;
}
}
//Notification calls
notification meter-statistics-updated {
- leaf meter-stats-id {
- type meter-stats-ref;
- }
- uses meter-stats-response;
+ leaf moreReplies {
+ type boolean;
+ }
+
+ uses inv:node;
+ uses meter-types:meter-statistics-reply;
uses tr:transaction-aware;
}
notification meter-config-stats-updated {
- leaf meter-config-id {
- type meter-config-ref;
- }
-
- uses meter-config-response;
- uses tr:transaction-aware;
+ leaf moreReplies {
+ type boolean;
+ }
+
+ uses inv:node;
+ uses meter-types:meter-config-stats-reply;
+ uses tr:transaction-aware;
}
notification meter-features-updated {
- leaf meter-features-id {
- type meter-features-ref;
- }
-
- uses meter-features-response;
+ leaf moreReplies {
+ type boolean;
+ }
+
+ uses inv:node;
+ uses meter-types:meter-features-reply;
uses tr:transaction-aware;
}
}
<module>inventory-manager</module>
<module>statistics-manager</module>
+ <module>forwardingrules-manager</module>
+
<!-- Compability Packages -->
<module>compatibility</module>
-
+ <module>zeromq-routingtable/implementation</module>
<module>sal-zeromq-connector</module>
</modules>
</activation>
<modules>
<module>sal-binding-it</module>
+ <module>zeromq-routingtable/integrationtest</module>
<module>clustered-data-store/integrationtest</module>
<module>test</module>
</modules>
public static def void setDelegate(RpcService proxy, RpcService delegate) {
val field = proxy.class.getField(DELEGATE_FIELD)
if (field == null) throw new UnsupportedOperationException("Unable to set delegate to proxy");
- if (field.type.isAssignableFrom(delegate.class)) {
+ if (delegate == null || field.type.isAssignableFrom(delegate.class)) {
field.set(proxy, delegate)
} else
throw new IllegalArgumentException("delegate class is not assignable to proxy");
public static def void setDelegate(Object proxy, Object delegate) {
val field = proxy.class.getField(DELEGATE_FIELD)
if (field == null) throw new UnsupportedOperationException("Unable to set delegate to proxy");
- if (field.type.isAssignableFrom(delegate.class)) {
+ if (delegate == null || field.type.isAssignableFrom(delegate.class)) {
field.set(proxy, delegate)
} else
throw new IllegalArgumentException("delegate class is not assignable to proxy");
import java.util.Set
import java.util.HashMap
import org.opendaylight.controller.sal.binding.spi.RpcRoutingTable
-import static org.opendaylight.controller.sal.binding.codegen.impl.XtendHelper.*
import org.opendaylight.yangtools.yang.binding.DataContainer
import org.opendaylight.yangtools.yang.binding.RpcImplementation
Class<C> cls) {
return new RpcRoutingTableImpl<>(cls);
}
-
- public static String foo() {
- return "Foo";
- }
}
import javassist.NotFoundException
import javassist.LoaderClassPath
import javassist.ClassClassPath
+import java.util.concurrent.locks.Lock
+import java.util.concurrent.locks.ReentrantLock
class JavassistUtils {
ClassPool classPool
+
+ @Property
+ val Lock lock = new ReentrantLock();
new(ClassPool pool) {
classPool = pool;
}
def CtClass createClass(String fqn, ClassGenerator cls) {
+
val target = classPool.makeClass(fqn);
cls.process(target);
return target;
}
def CtClass createClass(String fqn, CtClass superInterface, ClassGenerator cls) {
+
val target = classPool.makeClass(fqn);
target.implementsType(superInterface);
cls.process(target);
addField(field);
return field;
}
+
+ def CtField staticField(CtClass it, String name, Class<?> returnValue) {
+ val field = new CtField(returnValue.asCtClass, name, it);
+ field.modifiers = Modifier.PUBLIC + Modifier.STATIC
+ addField(field);
+ return field;
+ }
def get(ClassPool pool, Class<?> cls) {
try {
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface AugmentationCodec<A extends Augmentation<?>> extends DomCodec<A> {
+
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<A> input);
+
+ @Override
+ public ValueWithQName<A> deserialize(Node<?> input);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface ChoiceCaseCodec<C extends DataContainer> extends DataContainerCodec<C> {
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<C> input);
+
+ @Override
+ public ValueWithQName<C> deserialize(Node<?> input);
+
+ public boolean isAcceptable(Node<?> input);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface ChoiceCodec<C> extends DomCodec<C> {
+
+ @Override
+ public Node<?> serialize(ValueWithQName<C> input);
+
+ @Override
+ public ValueWithQName<C> deserialize(Node<?> input);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+import java.util.List;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentifierCodec;
+
+
+public interface CodecRegistry {
+
+ InstanceIdentifierCodec getInstanceIdentifierCodec();
+
+ <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(Class<T> object);
+
+ <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(Class<T> object);
+
+ <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(Class<T> object);
+
+ <T extends Augmentation<?>> AugmentationCodec<T> getCodecForAugmentation(Class<T> object);
+
+ Class<?> getClassForPath(List<QName> names);
+
+ IdentifierCodec<?> getKeyCodecForPath(List<QName> names);
+
+
+ void bindingClassEncountered(Class<?> cls);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface DataContainerCodec<T extends DataContainer> extends DomCodec<T> {
+
+
+ @Override
+ public ValueWithQName<T> deserialize(Node<?> input);
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<T> input);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface DomCodec<I> extends BindingCodec<Node<?>, ValueWithQName<I>>{
+
+
+ @Override
+ public Node<?> serialize(ValueWithQName<I> input);
+
+
+ @Override
+ public ValueWithQName<I> deserialize(Node<?> input);
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public interface IdentifierCodec<I extends Identifier<?>> extends DomCodec<I> {
+
+ @Override
+ public ValueWithQName<I> deserialize(Node<?> input);
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<I> input);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface InstanceIdentifierCodec extends BindingCodec<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier,InstanceIdentifier<?>> {
+
+ @Override
+ public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier serialize(InstanceIdentifier<?> input);
+
+ @Override
+ public InstanceIdentifier<?> deserialize(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier input);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.api;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class ValueWithQName<V> implements Entry<QName, V>{
+
+ final QName qname;
+ final V value;
+
+ public ValueWithQName(QName qname, V value) {
+ super();
+ this.qname = qname;
+ this.value = value;
+ }
+
+ public QName getQname() {
+ return qname;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public QName getKey() {
+ return qname;
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((qname == null) ? 0 : qname.hashCode());
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ @SuppressWarnings("rawtypes")
+ ValueWithQName other = (ValueWithQName) obj;
+ if (qname == null) {
+ if (other.qname != null)
+ return false;
+ } else if (!qname.equals(other.qname))
+ return false;
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return true;
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl;
+
+public interface BindingClassListener {
+
+ void onBindingClassCaptured(Class<?> cls);
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+import org.opendaylight.controller.sal.binding.dom.serializer.api.InstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public class CodecMapping {
+
+ public static final String INSTANCE_IDENTIFIER_CODEC = "INSTANCE_IDENTIFIER_CODEC";
+ public static final String CLASS_TO_CASE_MAP = "CLASS_TO_CASE";
+ public static final String COMPOSITE_TO_CASE = "COMPOSITE_TO_CASE";
+ public static final String AUGMENTATION_CODEC = "AUGMENTATION_CODEC";
+
+ public static void setIdentifierCodec(Class obj,InstanceIdentifierCodec codec) {
+ Field instanceIdField;
+ try {
+ instanceIdField = obj.getField(INSTANCE_IDENTIFIER_CODEC);
+ instanceIdField.set(null, codec);
+ } catch (NoSuchFieldException e) {
+ // NOOP
+ } catch (SecurityException e) {
+ // NOOP
+ } catch (IllegalAccessException e) {
+ // NOOp
+ }
+ }
+
+ public static void setClassToCaseMap(Class<? extends BindingCodec> codec,
+ Map<Class,BindingCodec> classToCaseRawCodec) {
+ Field instanceIdField;
+ try {
+ instanceIdField = codec.getField(CLASS_TO_CASE_MAP);
+ instanceIdField.set(null, classToCaseRawCodec);
+ } catch (NoSuchFieldException e) {
+ // NOOP
+ } catch (SecurityException e) {
+ // NOOP
+ } catch (IllegalAccessException e) {
+ // NOOp
+ }
+
+
+ }
+
+ public static void setCompositeNodeToCaseMap(Class<? extends BindingCodec> codec,
+ Map<CompositeNode,BindingCodec> compositeToCase) {
+ Field instanceIdField;
+ try {
+ instanceIdField = codec.getField(COMPOSITE_TO_CASE);
+ instanceIdField.set(null, compositeToCase);
+ } catch (NoSuchFieldException e) {
+ // NOOP
+ } catch (SecurityException e) {
+ // NOOP
+ } catch (IllegalAccessException e) {
+ // NOOp
+ }
+ }
+
+ public static void setAugmentationCodec(Class<? extends BindingCodec<Map<QName, Object>, Object>> dataCodec,
+ BindingCodec augmentableCodec) {
+ Field instanceIdField;
+ try {
+ instanceIdField = dataCodec.getField(AUGMENTATION_CODEC);
+ instanceIdField.set(null, augmentableCodec);
+ } catch (NoSuchFieldException e) {
+ // NOOP
+ } catch (SecurityException e) {
+ // NOOP
+ } catch (IllegalAccessException e) {
+ // NOOp
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl;
+
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+public class CodecTypeUtils {
+
+ @SuppressWarnings({"unchecked","rawtypes"})
+ public static IdentifiableItem<?, ?> newIdentifiableItem(Class<?> type, Object key) {
+ Class<? extends Identifiable<?>> identifiableType = (Class<? extends Identifiable<?>>) type;
+ Identifier<? extends Identifiable<?>> identifier = (Identifier<? extends Identifiable<?>>) key;
+ return new IdentifiableItem(identifiableType,identifier);
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public interface GeneratorListener {
+
+
+
+ void onCodecCreated(Class<?> codec);
+ void onValueCodecCreated(Class<?> valueClass,Class<?> valueCodec);
+ void onChoiceCodecCreated(Class<?> choiceClass,Class<? extends BindingCodec<Map<QName, Object>,Object>> choiceCodec);
+ void onCaseCodecCreated(Class<?> choiceClass,Class<? extends BindingCodec<Map<QName, Object>,Object>> choiceCodec);
+ public abstract void onDataContainerCodecCreated(Class<?> dataClass, Class<? extends BindingCodec<Map<QName, Object>,Object>> dataCodec);
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.opendaylight.controller.sal.binding.dom.serializer.api.CodecRegistry
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item
+import java.util.Map
+import java.util.WeakHashMap
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
+import java.util.ArrayList
+import org.opendaylight.controller.sal.binding.dom.serializer.api.InstanceIdentifierCodec
+import org.opendaylight.controller.sal.binding.dom.serializer.api.ValueWithQName
+import java.util.HashMap
+import org.slf4j.LoggerFactory
+import java.util.List
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentifierCodec
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import org.opendaylight.yangtools.yang.data.api.Node
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+
+class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
+
+ private static val LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl);
+ val CodecRegistry codecRegistry;
+
+ val Map<Class<?>,QName> classToQName = new WeakHashMap;
+
+
+ public new(CodecRegistry registry) {
+ codecRegistry = registry;
+ }
+
+
+ override deserialize(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier input) {
+ var Class<?> baType = null
+ val biArgs = input.path
+ val scannedPath = new ArrayList<QName>(biArgs.size);
+ val baArgs = new ArrayList<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument>(biArgs.size)
+ for(biArg : biArgs) {
+ scannedPath.add(biArg.nodeType);
+ val baArg = deserializePathArgument(biArg,scannedPath)
+ baArgs.add(baArg)
+ baType = baArg?.type
+ }
+ val ret = new InstanceIdentifier(baArgs,baType as Class<? extends DataObject>);
+ return ret;
+ }
+
+ private def dispatch org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument deserializePathArgument(NodeIdentifier argument,List<QName> processedPath) {
+ val Class cls = codecRegistry.getClassForPath(processedPath);
+ return new Item(cls);
+ }
+
+
+ private def dispatch org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument deserializePathArgument(NodeIdentifierWithPredicates argument,List<QName> processedPath) {
+ val Class type = codecRegistry.getClassForPath(processedPath);
+ val IdentifierCodec codec = codecRegistry.getIdentifierCodecForIdentifiable(type);
+ val value = codec.deserialize(argument.toCompositeNode())?.value;
+ return CodecTypeUtils.newIdentifiableItem(type,value);
+ }
+
+ def CompositeNode toCompositeNode(NodeIdentifierWithPredicates predicates) {
+ val keyValues = predicates.keyValues.entrySet;
+ val values = new ArrayList<Node<?>>(keyValues.size)
+ for(keyValue : keyValues) {
+ values.add(new SimpleNodeTOImpl(keyValue.key,null,keyValue.value))
+ }
+ return new CompositeNodeTOImpl(predicates.nodeType,null,values);
+ }
+
+ override serialize(InstanceIdentifier input) {
+ val pathArgs = input.path as List<org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument>
+ var QName previousQName = null;
+ val components = new ArrayList<PathArgument>(pathArgs.size);
+ for(baArg : pathArgs) {
+ codecRegistry.bindingClassEncountered(baArg.type);
+ val biArg = serializePathArgument(baArg,previousQName);
+ previousQName = biArg.nodeType;
+ components.add(biArg);
+ }
+ return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(components);
+ }
+
+ private def dispatch PathArgument serializePathArgument(Item argument, QName previousQname) {
+ val type = argument.type;
+ val qname = resolveQname(type);
+ if(previousQname == null) {
+ return new NodeIdentifier(qname);
+ }
+ return new NodeIdentifier(QName.create(previousQname,qname.localName));
+ }
+
+ private def dispatch PathArgument serializePathArgument(IdentifiableItem argument, QName previousQname) {
+ val Map<QName,Object> predicates = new HashMap();
+ val type = argument.type;
+ val keyCodec = codecRegistry.getIdentifierCodecForIdentifiable(type);
+ val qname = resolveQname(type);
+ val combinedInput = new ValueWithQName(previousQname,argument.key)
+ val compositeOutput = keyCodec.serialize(combinedInput as ValueWithQName);
+ for(outputValue :compositeOutput.value) {
+ predicates.put(outputValue.nodeType,outputValue.value);
+ }
+ if(previousQname == null) {
+ return new NodeIdentifierWithPredicates(qname,predicates);
+ }
+ return new NodeIdentifierWithPredicates(QName.create(previousQname,qname.localName),predicates);
+ }
+
+ def resolveQname(Class class1) {
+ val qname = classToQName.get(class1);
+ if(qname !== null) {
+ return qname;
+ }
+ val qnameField = class1.getField("QNAME");
+ val qnameValue = qnameField.get(null) as QName;
+ classToQName.put(class1,qnameValue);
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl
+
+import org.opendaylight.yangtools.yang.data.api.Node
+import java.util.Map
+import org.opendaylight.yangtools.yang.common.QName
+import java.util.List
+import java.util.ArrayList
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
+import com.google.common.base.Preconditions
+
+class IntermediateMapping {
+
+
+
+ static def Node<?> toNode(Map map) {
+ val nodeMap = map as Map<QName,Object>;
+ Preconditions.checkArgument(map.size == 1);
+ val elem = nodeMap.entrySet.iterator.next;
+ val qname = elem.key;
+ val value = elem.value;
+ toNodeImpl(qname, value);
+ }
+
+ static def dispatch Node<?> toNodeImpl(QName name, List<?> objects) {
+ val values = new ArrayList<Node<?>>(objects.size);
+ for (obj : objects) {
+ values.add(toNode(obj as Map));
+ }
+ return new CompositeNodeTOImpl(name, null, values);
+ }
+
+ static def dispatch Node<?> toNodeImpl(QName name, Map<QName, Object> object) {
+ throw new UnsupportedOperationException("Unsupported node hierarchy.");
+ }
+
+ static def dispatch Node<?> toNodeImpl(QName name, Object object) {
+ return new SimpleNodeTOImpl(name, null, object);
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import org.opendaylight.controller.sal.binding.dom.serializer.api.AugmentationCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.ChoiceCaseCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.ChoiceCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.CodecRegistry;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.DataContainerCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.DomCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.IdentifierCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.InstanceIdentifierCodec;
+import org.opendaylight.controller.sal.binding.dom.serializer.api.ValueWithQName;
+import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
+import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
+import org.opendaylight.yangtools.binding.generator.util.Types;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BindingCodec;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.*;
+import static org.opendaylight.controller.sal.binding.dom.serializer.impl.IntermediateMapping.*;
+
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
+import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+public class LazyGeneratedCodecRegistry implements //
+ CodecRegistry, //
+ SchemaServiceListener, //
+ GeneratorListener {
+
+ private final static Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
+ private final static LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
+
+ private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
+
+ private TransformerGenerator generator;
+
+ // Concrete class to codecs
+ private Map<Class<?>, DataContainerCodec<?>> containerCodecs = new WeakHashMap<>();
+ private Map<Class<?>, IdentifierCodec<?>> identifierCodecs = new WeakHashMap<>();
+ private Map<Class<?>, ChoiceCodecImpl<?>> choiceCodecs = new WeakHashMap<>();
+ private Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = new WeakHashMap<>();
+ private Map<Class<?>, AugmentableCompositeCodec> augmentableCodecs = new WeakHashMap<>();
+
+ /** Binding type to encountered classes mapping **/
+ @SuppressWarnings("rawtypes")
+ Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
+
+ @SuppressWarnings("rawtypes")
+ private ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseNodes = new ConcurrentHashMap<>();
+
+ private CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
+
+ Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
+
+ private SchemaContext currentSchema;
+
+ public TransformerGenerator getGenerator() {
+ return generator;
+ }
+
+ public void setGenerator(TransformerGenerator generator) {
+ this.generator = generator;
+ }
+
+ @Override
+ public InstanceIdentifierCodec getInstanceIdentifierCodec() {
+ return instanceIdentifierCodec;
+ }
+
+ @Override
+ public <T extends Augmentation<?>> AugmentationCodec<T> getCodecForAugmentation(Class<T> object) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Class<?> getClassForPath(List<QName> names) {
+ DataSchemaNode node = getSchemaNode(names);
+ SchemaPath path = node.getPath();
+ GeneratedTypeBuilder type = pathToType.get(path);
+ ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
+ @SuppressWarnings("rawtypes")
+ WeakReference<Class> weakRef = typeToClass.get(typeref);
+ return weakRef.get();
+ }
+
+ @Override
+ public IdentifierCodec<?> getKeyCodecForPath(List<QName> names) {
+ @SuppressWarnings("unchecked")
+ Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
+ return getIdentifierCodecForIdentifiable(cls);
+ }
+
+ @Override
+ public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(Class<T> type) {
+ @SuppressWarnings("unchecked")
+ DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
+ if (ret != null) {
+ return ret;
+ }
+ Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
+ BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
+ DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
+ containerCodecs.put(type, newWrapper);
+ return newWrapper;
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public void bindingClassEncountered(Class cls) {
+ ConcreteType typeRef = Types.typeForClass(cls);
+ WeakReference<Class> weakRef = new WeakReference<>(cls);
+ typeToClass.put(typeRef, weakRef);
+ }
+
+ private DataSchemaNode getSchemaNode(List<QName> path) {
+ QName firstNode = path.get(0);
+ DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
+ firstNode.getRevision());
+ Iterator<QName> iterator = path.iterator();
+ while (iterator.hasNext()) {
+ QName arg = iterator.next();
+ DataSchemaNode currentNode = previous.getDataChildByName(arg);
+ if (currentNode == null && previous instanceof DataNodeContainer) {
+ currentNode = searchInChoices(previous, arg);
+ }
+ if (currentNode instanceof DataNodeContainer) {
+ previous = (DataNodeContainer) currentNode;
+ } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
+ checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
+ return currentNode;
+ }
+ }
+ return (DataSchemaNode) previous;
+ }
+
+ private DataSchemaNode searchInChoices(DataNodeContainer node, QName arg) {
+ Set<DataSchemaNode> children = node.getChildNodes();
+ for (DataSchemaNode child : children) {
+ if (child instanceof ChoiceNode) {
+ ChoiceNode choiceNode = (ChoiceNode) child;
+ DataSchemaNode potential = searchInCases(choiceNode, arg);
+ if (potential != null) {
+ return potential;
+ }
+ }
+ }
+ return null;
+ }
+
+ private DataSchemaNode searchInCases(ChoiceNode choiceNode, QName arg) {
+ Set<ChoiceCaseNode> cases = choiceNode.getCases();
+ for (ChoiceCaseNode caseNode : cases) {
+ DataSchemaNode node = caseNode.getDataChildByName(arg);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ private <T> T newInstanceOf(Class<?> newType) {
+ try {
+ @SuppressWarnings("unchecked")
+ T ret = (T) newType.newInstance();
+ return ret;
+ } catch (InstantiationException e) {
+ throw new IllegalStateException(e);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(Class<T> type) {
+ IdentifierCodec<?> obj = identifierCodecs.get(type);
+ if (obj != null) {
+ return obj;
+ }
+ Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
+ .keyTransformerForIdentifiable(type);
+ BindingCodec<Map<QName, Object>, Object> newInstance;
+ newInstance = newInstanceOf(newCodec);
+ IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
+ identifierCodecs.put(type, newWrapper);
+ return newWrapper;
+ }
+
+ @Override
+ public void onCodecCreated(Class<?> cls) {
+ CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
+ }
+
+ @Override
+ public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(Class<T> object) {
+ @SuppressWarnings("unchecked")
+ IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
+ if (obj != null) {
+ return obj;
+ }
+ Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
+ .keyTransformerForIdentifier(object);
+ BindingCodec<Map<QName, Object>, Object> newInstance;
+ newInstance = newInstanceOf(newCodec);
+ IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
+ identifierCodecs.put(object, newWrapper);
+ return newWrapper;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public ChoiceCaseCodecImpl getCaseCodecFor(Class caseClass) {
+ ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
+ if (potential != null) {
+ return potential;
+ }
+ ConcreteType typeref = Types.typeForClass(caseClass);
+ ChoiceCaseCodecImpl caseCodec = typeToCaseNodes.get(typeref);
+
+ @SuppressWarnings("unchecked")
+ Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.schema);
+ BindingCodec newInstance = newInstanceOf(newCodec);
+ caseCodec.setDelegate(newInstance);
+ caseCodecs.put(caseClass, caseCodec);
+
+ for (Entry<Class<?>, ChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
+ if (choice.getKey().isAssignableFrom(caseClass)) {
+ choice.getValue().cases.put(caseClass, caseCodec);
+ }
+ }
+ return caseCodec;
+ }
+
+ public void onModuleContextAdded(SchemaContext schemaContext, Module module, ModuleContext context) {
+ pathToType.putAll(context.getChildNodes());
+
+ captureCases(context.getCases(), schemaContext);
+ }
+
+ private void captureCases(Map<SchemaPath, GeneratedTypeBuilder> cases, SchemaContext module) {
+ for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
+ ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
+ .getValue().getName());
+ ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
+ @SuppressWarnings("rawtypes")
+ ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
+ typeToCaseNodes.putIfAbsent(typeref, value);
+ }
+ }
+
+ @Override
+ public void onGlobalContextUpdated(SchemaContext context) {
+ currentSchema = context;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public void onChoiceCodecCreated(Class<?> choiceClass,
+ Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
+ ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
+ checkState(oldCodec == null);
+ BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
+ ChoiceCodecImpl<?> newCodec = new ChoiceCodecImpl(delegate);
+ choiceCodecs.put(choiceClass, newCodec);
+ CodecMapping.setClassToCaseMap(choiceCodec, (Map<Class, BindingCodec>) classToCaseRawCodec);
+ CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
+
+ }
+
+ @Override
+ public void onValueCodecCreated(Class<?> valueClass, Class<?> valueCodec) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onCaseCodecCreated(Class<?> choiceClass,
+ Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onDataContainerCodecCreated(Class<?> dataClass,
+ Class<? extends BindingCodec<Map<QName, Object>, Object>> dataCodec) {
+ if (Augmentable.class.isAssignableFrom(dataClass)) {
+ AugmentableCompositeCodec augmentableCodec = getAugmentableCodec(dataClass);
+ CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
+ }
+
+ }
+
+ private AugmentableCompositeCodec getAugmentableCodec(Class<?> dataClass) {
+ AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass);
+ if (ret != null) {
+ return ret;
+ }
+ ret = new AugmentableCompositeCodec(dataClass);
+ augmentableCodecs.put(dataClass, ret);
+ return ret;
+ }
+
+ private static abstract class IntermediateCodec<T> implements //
+ DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
+
+ private final BindingCodec<Map<QName, Object>, Object> delegate;
+
+ @Override
+ public BindingCodec<Map<QName, Object>, Object> getDelegate() {
+ return delegate;
+ }
+
+ public IntermediateCodec(BindingCodec<Map<QName, Object>, Object> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Node<?> serialize(ValueWithQName<T> input) {
+ Map<QName, Object> intermediateOutput = delegate.serialize(input);
+ return toNode(intermediateOutput);
+ }
+ }
+
+ private static class IdentifierCodecImpl<T extends Identifier<?>> //
+ extends IntermediateCodec<T> //
+ implements IdentifierCodec<T> {
+
+ public IdentifierCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public ValueWithQName<T> deserialize(Node<?> input) {
+ QName qname = input.getNodeType();
+ @SuppressWarnings("unchecked")
+ T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
+ return new ValueWithQName<T>(qname, value);
+ }
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<T> input) {
+ return (CompositeNode) super.serialize(input);
+ }
+ }
+
+ private static class DataContainerCodecImpl<T extends DataContainer> //
+ extends IntermediateCodec<T> //
+ implements DataContainerCodec<T> {
+
+ public DataContainerCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public ValueWithQName<T> deserialize(Node<?> input) {
+ if (input == null) {
+ return null;
+ }
+ QName qname = input.getNodeType();
+ @SuppressWarnings("unchecked")
+ T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
+ return new ValueWithQName<T>(qname, value);
+ }
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<T> input) {
+ return (CompositeNode) super.serialize(input);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
+ Delegator<BindingCodec> {
+ private final boolean augmenting;
+ private BindingCodec delegate;
+
+ private final Set<String> validNames;
+ private final Set<QName> validQNames;
+ private ChoiceCaseNode schema;
+
+ public ChoiceCaseCodecImpl(ChoiceCaseNode caseNode) {
+ this.delegate = NOT_READY_CODEC;
+ this.schema = caseNode;
+ validNames = new HashSet<>();
+ validQNames = new HashSet<>();
+ for (DataSchemaNode node : caseNode.getChildNodes()) {
+ QName qname = node.getQName();
+ validQNames.add(qname);
+ validNames.add(qname.getLocalName());
+ }
+ augmenting = caseNode.isAugmenting();
+ }
+
+ @Override
+ public ValueWithQName<T> deserialize(Node<?> input) {
+ throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
+ }
+
+ @Override
+ public CompositeNode serialize(ValueWithQName<T> input) {
+ throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
+ }
+
+ public BindingCodec getDelegate() {
+ return delegate;
+ }
+
+ public void setDelegate(BindingCodec delegate) {
+ this.delegate = delegate;
+ }
+
+ public ChoiceCaseNode getSchema() {
+ return schema;
+ }
+
+ @Override
+ public boolean isAcceptable(Node<?> input) {
+ if (false == (input instanceof CompositeNode)) {
+ if (augmenting) {
+ return checkAugmenting((CompositeNode) input);
+ } else {
+ return checkLocal((CompositeNode) input);
+ }
+ }
+ return false;
+ }
+
+ private boolean checkLocal(CompositeNode input) {
+ QName parent = input.getNodeType();
+ for (Node<?> childNode : input.getChildren()) {
+ QName child = childNode.getNodeType();
+ if (false == Objects.equals(parent.getNamespace(), child.getNamespace())) {
+ continue;
+ }
+ if (false == Objects.equals(parent.getRevision(), child.getRevision())) {
+ continue;
+ }
+ if (validNames.contains(child.getLocalName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkAugmenting(CompositeNode input) {
+ for (Node<?> child : input.getChildren()) {
+ if (validQNames.contains(child.getNodeType())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private static class ChoiceCodecImpl<T> implements ChoiceCodec<T> {
+
+ private final BindingCodec<Map<QName, Object>, Object> delegate;
+
+ @SuppressWarnings("rawtypes")
+ private final Map<Class, ChoiceCaseCodecImpl<?>> cases = new WeakHashMap<>();
+
+ private final CaseCompositeNodeMapFacade CompositeToCase;
+
+ public ChoiceCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
+ this.delegate = delegate;
+ this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
+ }
+
+ @Override
+ public ValueWithQName<T> deserialize(Node<?> input) {
+ throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
+ }
+
+ @Override
+ public Node<?> serialize(ValueWithQName<T> input) {
+ throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
+ }
+
+ public CaseCompositeNodeMapFacade getCompositeToCase() {
+ return CompositeToCase;
+ }
+
+ public Map<Class, ChoiceCaseCodecImpl<?>> getCases() {
+ return cases;
+ }
+
+ public BindingCodec<Map<QName, Object>, Object> getDelegate() {
+ return delegate;
+ }
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ private class CaseClassMapFacade extends MapFacadeBase {
+
+ @Override
+ public Set<java.util.Map.Entry<Class, BindingCodec<Object, Object>>> entrySet() {
+ return null;
+ }
+
+ @Override
+ public BindingCodec get(Object key) {
+ if (key instanceof Class) {
+ Class cls = (Class) key;
+ bindingClassEncountered(cls);
+ ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
+ return caseCodec.getDelegate();
+ }
+ return null;
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
+
+ final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
+
+ public CaseCompositeNodeMapFacade(Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
+ this.choiceCases = choiceCases;
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<CompositeNode, BindingCodec>> entrySet() {
+ return null;
+ }
+
+ @Override
+ public BindingCodec get(Object key) {
+ if (false == (key instanceof CompositeNode)) {
+ return null;
+ }
+ for (java.util.Map.Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
+ ChoiceCaseCodecImpl<?> codec = entry.getValue();
+ if (codec.isAcceptable((CompositeNode) key)) {
+ return codec.getDelegate();
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * This map is used as only facade for {@link BindingCodec} in different
+ * classloaders to retrieve codec dynamicly based on provided key.
+ *
+ * @param <T>
+ * Key type
+ */
+ @SuppressWarnings("rawtypes")
+ private static abstract class MapFacadeBase<T> implements Map<T, BindingCodec> {
+
+ @Override
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ @Override
+ public void clear() {
+ throw notModifiable();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ @Override
+ public BindingCodec remove(Object key) {
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public Collection<BindingCodec> values() {
+ return null;
+ }
+
+ private UnsupportedOperationException notModifiable() {
+ return new UnsupportedOperationException("Not externally modifiable.");
+ }
+
+ @Override
+ public BindingCodec<Map<QName, Object>, Object> put(T key, BindingCodec value) {
+ throw notModifiable();
+ }
+
+ @Override
+ public void putAll(Map<? extends T, ? extends BindingCodec> m) {
+ throw notModifiable();
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public Set<T> keySet() {
+ return null;
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<T, BindingCodec>> entrySet() {
+ return null;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return false;
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private class AugmentableCompositeCodec implements BindingCodec {
+
+ private final Class augmentableType;
+
+ Map<Class, BindingCodec> rawAugmentationCodecs = new WeakHashMap<>();
+
+ public AugmentableCompositeCodec(Class type) {
+ checkArgument(Augmentable.class.isAssignableFrom(type));
+ augmentableType = type;
+ }
+
+ @Override
+ public Object serialize(Object input) {
+ if (input instanceof Augmentable<?>) {
+
+ Map<Class, Augmentation> augmentations = getAugmentations(input);
+ return serializeImpl(augmentations);
+ }
+ return null;
+ }
+
+ private Map<Class, Augmentation> getAugmentations(Object input) {
+ Field augmentationField;
+ try {
+ augmentationField = input.getClass().getDeclaredField("augmentation");
+ augmentationField.setAccessible(true);
+ Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
+ return augMap;
+ } catch (NoSuchFieldException e) {
+
+ } catch (SecurityException e) {
+
+ } catch (IllegalArgumentException e) {
+
+ } catch (IllegalAccessException e) {
+
+ }
+ return Collections.emptyMap();
+ }
+
+ private List serializeImpl(Map<Class, Augmentation> input) {
+ List ret = new ArrayList<>();
+ for (Entry<Class, Augmentation> entry : input.entrySet()) {
+ BindingCodec codec = getRawCodecForAugmentation(entry.getKey());
+ List output = (List) codec.serialize(new ValueWithQName(null, entry.getValue()));
+ ret.addAll(output);
+ }
+ return ret;
+ }
+
+ private BindingCodec getRawCodecForAugmentation(Class key) {
+ BindingCodec ret = rawAugmentationCodecs.get(key);
+ if (ret != null) {
+ return ret;
+ }
+ try {
+ Class<? extends BindingCodec> retClass = generator.augmentationTransformerFor(key);
+ ret = retClass.newInstance();
+ rawAugmentationCodecs.put(key, ret);
+ return ret;
+ } catch (InstantiationException e) {
+
+ } catch (IllegalAccessException e) {
+
+ }
+ return null;
+ }
+
+ @Override
+ public Map<Class, Augmentation> deserialize(Object input) {
+ Map<Class, Augmentation> ret = new HashMap<>();
+ if (input instanceof CompositeNode) {
+ for (Entry<Class, BindingCodec> codec : rawAugmentationCodecs.entrySet()) {
+ Augmentation value = (Augmentation) codec.getValue().deserialize(input);
+ if (value != null) {
+ ret.put(codec.getKey(), value);
+ }
+ }
+ }
+ return ret;
+ }
+
+ public Map<Class, BindingCodec> getRawAugmentationCodecs() {
+ return rawAugmentationCodecs;
+ }
+
+ public void setRawAugmentationCodecs(Map<Class, BindingCodec> rawAugmentationCodecs) {
+ this.rawAugmentationCodecs = rawAugmentationCodecs;
+ }
+
+ public Class getAugmentableType() {
+ return augmentableType;
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
+
+ private BindingCodec delegate;
+
+ @Override
+ public BindingCodec getDelegate() {
+ if (delegate == null) {
+ throw new IllegalStateException("Codec not initialized yet.");
+ }
+ return delegate;
+ }
+
+ @Override
+ public Object deserialize(Object input) {
+ return getDelegate().deserialize(input);
+ }
+
+ @Override
+ public Object serialize(Object input) {
+ return getDelegate().serialize(input);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl
+
+import org.opendaylight.controller.sal.binding.dom.serializer.impl.TransformerGenerator
+import javassist.ClassPool
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl
+import java.util.Map
+import org.opendaylight.yangtools.sal.binding.model.api.Type
+import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
+import org.opendaylight.yangtools.yang.model.api.SchemaNode
+import java.util.concurrent.ConcurrentHashMap
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import java.util.Map.Entry
+import java.util.AbstractMap.SimpleEntry
+import org.opendaylight.yangtools.yang.model.api.SchemaPath
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
+import java.util.ArrayList
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.binding.DataContainer
+import static com.google.common.base.Preconditions.*;
+import java.util.List
+import org.opendaylight.yangtools.yang.data.api.Node
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
+import org.opendaylight.yangtools.concepts.Delegator
+import java.util.concurrent.ConcurrentMap
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
+import org.opendaylight.yangtools.yang.binding.BindingCodec
+import com.google.common.collect.HashMultimap
+import com.google.common.util.concurrent.SettableFuture
+import java.util.concurrent.Future
+import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
+import org.opendaylight.controller.sal.binding.dom.serializer.impl.LazyGeneratedCodecRegistry
+import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService
+import org.slf4j.LoggerFactory
+import org.opendaylight.controller.sal.binding.dom.serializer.api.ValueWithQName
+import org.opendaylight.controller.sal.binding.dom.serializer.api.DataContainerCodec
+import org.opendaylight.yangtools.binding.generator.util.Types
+
+class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener {
+
+ @Property
+ ClassPool pool;
+
+ private static val LOG = LoggerFactory.getLogger(RuntimeGeneratedMappingServiceImpl);
+
+ @Property
+ extension TransformerGenerator binding;
+
+ @Property
+ extension LazyGeneratedCodecRegistry registry;
+
+ @Property
+ val ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap();
+
+ @Property
+ val ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
+
+ @Property
+ val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
+
+ val promisedTypeDefinitions = HashMultimap.<Type, SettableFuture<GeneratedTypeBuilder>>create;
+
+ val promisedSchemas = HashMultimap.<Type, SettableFuture<SchemaNode>>create;
+
+ override onGlobalContextUpdated(SchemaContext arg0) {
+ recreateBindingContext(arg0);
+ registry.onGlobalContextUpdated(arg0);
+ }
+
+ def recreateBindingContext(SchemaContext schemaContext) {
+ val newBinding = new BindingGeneratorImpl();
+ newBinding.generateTypes(schemaContext);
+
+ for (entry : newBinding.moduleContexts.entrySet) {
+
+ registry.onModuleContextAdded(schemaContext, entry.key, entry.value);
+
+ //val module = entry.key;
+ val context = entry.value;
+ updateBindingFor(context.childNodes, schemaContext);
+ updateBindingFor(context.cases, schemaContext);
+
+
+ val typedefs = context.typedefs;
+ for (typedef : typedefs.values) {
+ binding.typeDefinitions.put(typedef, typedef as GeneratedType);
+ }
+ val augmentations = context.augmentations;
+ for (augmentation : augmentations) {
+ binding.typeToDefinition.put(augmentation, augmentation);
+ }
+
+ binding.typeToAugmentation.putAll(context.typeToAugmentation);
+ }
+ }
+
+ override CompositeNode toDataDom(DataObject data) {
+ toCompositeNodeImpl(data);
+ }
+
+ override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
+ Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
+ val key = toDataDom(entry.key)
+ val data = toCompositeNodeImpl(entry.value);
+ return new SimpleEntry(key, data);
+ }
+
+ private def CompositeNode toCompositeNodeImpl(DataObject object) {
+ val cls = object.implementedInterface;
+ waitForSchema(cls);
+ val codec = registry.getCodecForDataObject(cls) as DataContainerCodec<DataObject>;
+ val ret = codec.serialize(new ValueWithQName(null, object));
+ return ret as CompositeNode;
+ }
+
+ private def waitForSchema(Class<? extends DataContainer> class1) {
+ val ref = Types.typeForClass(class1);
+ getSchemaWithRetry(ref);
+ }
+
+ override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
+ InstanceIdentifier<? extends DataObject> path) {
+ for (arg : path.path) {
+ waitForSchema(arg.type);
+ }
+ return registry.instanceIdentifierCodec.serialize(path);
+ }
+
+ override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode node) {
+ if (node == null) {
+ return null;
+ }
+ val targetType = path.targetType
+ val transformer = registry.getCodecForDataObject(targetType);
+ val ret = transformer.deserialize(node)?.value as DataObject;
+ return ret;
+ }
+
+ private def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
+ for (entry : map.entrySet) {
+ val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
+ //LOG.info("{} : {}",entry.key,entry.value.fullyQualifiedName)
+ if (schemaNode != null) {
+ typeToSchemaNode.put(entry.value, schemaNode);
+ typeToDefinition.put(entry.value, entry.value);
+ updatePromisedSchemas(entry.value, schemaNode);
+ }
+ }
+ }
+
+ public def void start() {
+ binding = new TransformerGenerator(pool);
+ registry = new LazyGeneratedCodecRegistry()
+ registry.generator = binding
+
+ //binding.staticFieldsInitializer = registry
+ binding.listener = registry
+ binding.typeToDefinition = typeToDefinition
+ binding.typeToSchemaNode = typeToSchemaNode
+ binding.typeDefinitions = typeDefinitions
+
+ }
+
+ private def getTypeDefinition(Type type) {
+ val typeDef = typeToDefinition.get(type);
+ if (typeDef !== null) {
+ return typeDef;
+ }
+ return type.getTypeDefInFuture.get();
+ }
+
+ private def Future<GeneratedTypeBuilder> getTypeDefInFuture(Type type) {
+ val future = SettableFuture.<GeneratedTypeBuilder>create()
+ promisedTypeDefinitions.put(type, future);
+ return future;
+ }
+
+ private def void updatePromisedTypeDefinitions(GeneratedTypeBuilder builder) {
+ val futures = promisedTypeDefinitions.get(builder);
+ if (futures === null || futures.empty) {
+ return;
+ }
+ for (future : futures) {
+ future.set(builder);
+ }
+ promisedTypeDefinitions.removeAll(builder);
+ }
+
+ private def getSchemaWithRetry(Type type) {
+ val typeDef = typeToSchemaNode.get(type);
+ if (typeDef !== null) {
+ return typeDef;
+ }
+ return type.getSchemaInFuture.get();
+ }
+
+ private def Future<SchemaNode> getSchemaInFuture(Type type) {
+ val future = SettableFuture.<SchemaNode>create()
+ promisedSchemas.put(type, future);
+ return future;
+ }
+
+ private def void updatePromisedSchemas(Type builder, SchemaNode schema) {
+ val ref = new ReferencedTypeImpl(builder.packageName, builder.name);
+ val futures = promisedSchemas.get(ref);
+ if (futures === null || futures.empty) {
+ return;
+ }
+ for (future : futures) {
+ future.set(schema);
+ }
+ promisedSchemas.removeAll(builder);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.dom.serializer.impl;
+
+public interface StaticFieldInitializer {
+
+ void initializeStaticFields(Class<?> cls);
+}
import org.opendaylight.yangtools.yang.common.QName
import javassist.CtField
import static javassist.Modifier.*
+import static org.opendaylight.controller.sal.binding.dom.serializer.impl.CodecMapping.*
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
-import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
import org.opendaylight.yangtools.sal.binding.model.api.Type
import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
-import java.util.WeakHashMap
import java.util.List
import java.util.TreeSet
import com.google.common.base.Joiner
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
import static org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils.*;
import org.opendaylight.yangtools.yang.binding.BindingDeserializer
-import org.opendaylight.yangtools.yang.binding.BindingSerializer
import org.opendaylight.yangtools.yang.binding.BindingCodec
import org.slf4j.LoggerFactory
import org.opendaylight.controller.sal.binding.codegen.CodeGenerationException
import org.opendaylight.yangtools.yang.model.api.ChoiceNode
import java.security.ProtectionDomain
import java.io.File
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
+import java.util.Map.Entry
+import java.util.AbstractMap.SimpleEntry
+import org.opendaylight.yangtools.yang.binding.DataObject
+import org.opendaylight.yangtools.yang.binding.Augmentation
+import java.util.Iterator
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema
+import java.util.concurrent.ConcurrentHashMap
class TransformerGenerator {
public static val STRING = Types.typeForClass(String);
public static val BOOLEAN = Types.typeForClass(Boolean);
public static val INTEGER = Types.typeForClass(Integer);
+ public static val INSTANCE_IDENTIFIER = Types.typeForClass(InstanceIdentifier)
//public static val DECIMAL = Types.typeForClass(Decimal);
public static val LONG = Types.typeForClass(Long);
val ClassPool classPool
val extension JavassistUtils utils;
- CtClass ctTransformator
+ CtClass BINDING_CODEC
CtClass ctQName
@Property
var File classFileCapturePath;
+ @Property
+ var Map<Type, Type> typeDefinitions = new ConcurrentHashMap();
@Property
- var Map<Type, Type> typeDefinitions;
+ var Map<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
@Property
- var Map<Type, GeneratedTypeBuilder> typeToDefinition
+ var Map<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
@Property
- var Map<Type, SchemaNode> typeToSchemaNode
+ var Map<Type, AugmentationSchema> typeToAugmentation = new ConcurrentHashMap();
- val Map<Class<?>, Class<?>> generatedClasses = new WeakHashMap();
+ @Property
+ var GeneratorListener listener;
public new(ClassPool pool) {
classPool = pool;
utils = new JavassistUtils(pool)
- ctTransformator = BindingCodec.asCtClass;
+ BINDING_CODEC = BindingCodec.asCtClass;
ctQName = QName.asCtClass
}
def Class<? extends BindingCodec<Map<QName, Object>, Object>> transformerFor(Class<?> inputType) {
- return withClassLoader(inputType.classLoader) [ |
- val ret = generatedClasses.get(inputType);
+ return withClassLoaderAndLock(inputType.classLoader, lock) [ |
+ val ret = getGeneratedClass(inputType)
if (ret !== null) {
return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
}
val node = typeToSchemaNode.get(ref)
val typeSpecBuilder = typeToDefinition.get(ref)
val typeSpec = typeSpecBuilder.toInstance();
- val newret = generateTransformerFor(inputType, typeSpec, node)
- generatedClasses.put(inputType, newret);
+ val newret = generateTransformerFor(inputType, typeSpec, node);
+ return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ ]
+ }
+
+ def Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentationTransformerFor(Class<?> inputType) {
+ return withClassLoaderAndLock(inputType.classLoader, lock) [ |
+ val ret = getGeneratedClass(inputType)
+ if (ret !== null) {
+ return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ }
+ val ref = Types.typeForClass(inputType)
+ val node = typeToAugmentation.get(ref)
+ val typeSpecBuilder = typeToDefinition.get(ref)
+ val typeSpec = typeSpecBuilder.toInstance();
+ val newret = generateAugmentationTransformerFor(inputType, typeSpec, node);
+ return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ ]
+ }
+
+ def Class<? extends BindingCodec<Object, Object>> caseCodecFor(Class<?> inputType, ChoiceCaseNode node) {
+ return withClassLoaderAndLock(inputType.classLoader, lock) [ |
+ val ret = getGeneratedClass(inputType)
+ if (ret !== null) {
+ return ret as Class<? extends BindingCodec<Object, Object>>;
+ }
+ val ref = Types.typeForClass(inputType)
+ val typeSpecBuilder = typeToDefinition.get(ref)
+ val typeSpec = typeSpecBuilder.toInstance();
+ val newret = generateCaseCodec(inputType, typeSpec, node);
+ return newret as Class<? extends BindingCodec<Object, Object>>;
+ ]
+ }
+
+ def Class<? extends BindingCodec<Map<QName, Object>, Object>> keyTransformerForIdentifiable(Class<?> parentType) {
+ return withClassLoaderAndLock(parentType.classLoader, lock) [ |
+ val inputName = parentType.name + "Key";
+ val inputType = loadClassWithTCCL(inputName);
+ val ret = getGeneratedClass(inputType)
+ if (ret !== null) {
+ return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ }
+ val ref = Types.typeForClass(parentType)
+ val node = typeToSchemaNode.get(ref) as ListSchemaNode
+ val typeSpecBuilder = typeToDefinition.get(ref)
+ val typeSpec = typeSpecBuilder.identifierDefinition;
+ val newret = generateKeyTransformerFor(inputType, typeSpec, node);
+ return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ ]
+ }
+
+ def getIdentifierDefinition(GeneratedTypeBuilder builder) {
+ val inst = builder.toInstance
+ val keyMethod = inst.methodDefinitions.findFirst[name == "getKey"]
+ return keyMethod.returnType as GeneratedTransferObject
+ }
+
+ def Class<? extends BindingCodec<Map<QName, Object>, Object>> keyTransformerForIdentifier(Class<?> inputType) {
+ return withClassLoaderAndLock(inputType.classLoader, lock) [ |
+ val ret = getGeneratedClass(inputType)
+ if (ret !== null) {
+ return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ }
+ val ref = Types.typeForClass(inputType)
+ val node = typeToSchemaNode.get(ref) as ListSchemaNode
+ val typeSpecBuilder = typeToDefinition.get(ref)
+ val typeSpec = typeSpecBuilder.toInstance();
+ val newret = generateKeyTransformerFor(inputType, typeSpec, node);
return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
]
}
private def Class<?> keyTransformerFor(Class<?> inputType, GeneratedType type, ListSchemaNode schema) {
- return withClassLoader(inputType.classLoader) [ |
- val transformer = generatedClasses.get(inputType);
+ return withClassLoaderAndLock(inputType.classLoader, lock) [ |
+ val transformer = getGeneratedClass(inputType)
if (transformer != null) {
return transformer;
}
val newret = generateKeyTransformerFor(inputType, type, schema);
- generatedClasses.put(inputType, newret);
return newret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
]
}
- def Class<?> keyTransformer(GeneratedType type, ListSchemaNode node) {
+ private def Class getGeneratedClass(Class<? extends Object> cls) {
+
+ try {
+ return loadClassWithTCCL(cls.codecClassName)
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ private def Class<?> keyTransformer(GeneratedType type, ListSchemaNode node) {
val cls = loadClassWithTCCL(type.resolvedName + "Key");
keyTransformerFor(cls, type, node);
}
private def serializer(Type type) {
val cls = loadClassWithTCCL(type.resolvedName);
+
transformerFor(cls);
}
private def Class<?> getValueSerializer(GeneratedTransferObject type) {
val cls = loadClassWithTCCL(type.resolvedName);
- val transformer = generatedClasses.get(cls);
+ val transformer = cls.generatedClass;
if (transformer !== null) {
return transformer;
}
val valueTransformer = generateValueTransformer(cls, type);
- generatedClasses.put(cls, valueTransformer);
return valueTransformer;
}
private def generateKeyTransformerFor(Class<? extends Object> inputType, GeneratedType typeSpec, ListSchemaNode node) {
try {
- log.info("Generating DOM Codec for {} with {}",inputType,inputType.classLoader)
+ log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
val properties = typeSpec.allProperties;
- val ctCls = createClass(inputType.transformatorFqn) [
+ val ctCls = createClass(inputType.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
staticQNameField(node.QName);
- implementsType(ctTransformator)
+ implementsType(BINDING_CODEC)
method(Object, "toDomStatic", QName, Object) [
modifiers = PUBLIC + FINAL + STATIC
body = '''
{
-
- return null;
+ «QName.name» _resultName;
+ if($1 != null) {
+ _resultName = «QName.name».create($1,QNAME.getLocalName());
+ } else {
+ _resultName = QNAME;
+ }
+ java.util.List _childNodes = new java.util.ArrayList();
+ «inputType.name» value = («inputType.name») $2;
+ «FOR key : node.keyDefinition»
+ «val propertyName = key.getterName»
+ «val keyDef = node.getDataChildByName(key)»
+ «val property = properties.get(propertyName)»
+ «serializeProperty(keyDef, property, propertyName)»;
+ «ENDFOR»
+ return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
}
'''
]
«val propertyName = key.getterName»
«val keyDef = node.getDataChildByName(key)»
«val property = properties.get(propertyName)»
- «deserializeProperty(keyDef, property.returnType, property)»;
+ «deserializeProperty(keyDef, property, propertyName)»;
«ENDFOR»
«inputType.name» _value = new «inputType.name»(«node.keyDefinition.keyConstructorList»);
return _value;
]
method(Object, "serialize", Object) [
body = '''
- return toDomStatic(QNAME,$1);
+ {
+ java.util.Map.Entry _input = (java.util.Map.Entry) $1;
+ «QName.name» _localQName = («QName.name») _input.getKey();
+ «inputType.name» _keyValue = («inputType.name») _input.getValue();
+ return toDomStatic(_localQName,_keyValue);
+ }
'''
]
method(Object, "deserialize", Object) [
]
]
val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
- log.info("DOM Codec for {} was generated {}",inputType,ret)
+ log.info("DOM Codec for {} was generated {}", inputType, ret)
return ret as Class<? extends BindingCodec<Map<QName,Object>, ?>>;
} catch (Exception e) {
- processException(inputType,e);
+ processException(inputType, e);
return null;
}
}
- private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(Class inputType,
- GeneratedType typeSpec, SchemaNode node) {
+ private def Class<? extends BindingCodec<Object, Object>> generateCaseCodec(Class inputType, GeneratedType type,
+ ChoiceCaseNode node) {
try {
- log.info("Generating DOM Codec for {} with {}",inputType,inputType.classLoader)
- val ctCls = createClass(typeSpec.transformatorFqn) [
+ log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
+ val ctCls = createClass(type.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
+ implementsType(BINDING_CODEC)
staticQNameField(inputType);
- implementsType(ctTransformator)
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
+ staticField(it, AUGMENTATION_CODEC, BindingCodec)
+ method(Object, "toDomStatic", QName, Object) [
+ modifiers = PUBLIC + FINAL + STATIC
+ body = '''
+ {
+ «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
+ java.util.List _childNodes = new java.util.ArrayList();
+ «type.resolvedName» value = («type.resolvedName») $2;
+ «transformDataContainerBody(type.allProperties, node)»
+ return ($r) _childNodes;
+ }
+ '''
+ ]
+ method(Object, "serialize", Object) [
+ body = '''
+ {
+ java.util.Map.Entry _input = (java.util.Map.Entry) $1;
+ «QName.name» _localName = QNAME;
+ if(_input.getKey() != null) {
+ _localName = («QName.name») _input.getKey();
+ }
+ return toDomStatic(_localName,_input.getValue());
+ }
+ '''
+ ]
+ method(Object, "fromDomStatic", QName, Object) [
+ modifiers = PUBLIC + FINAL + STATIC
+ body = deserializeBody(type, node)
+ ]
+ method(Object, "deserialize", Object) [
+ body = '''
+ {
+
+ return fromDomStatic(QNAME,$1);
+ }
+ '''
+ ]
+ ]
+
+ val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
+ log.info("DOM Codec for {} was generated {}", inputType, ret)
+ return ret as Class<? extends BindingCodec<Object, Object>>;
+ } catch (Exception e) {
+ processException(inputType, e);
+ return null;
+ }
+ }
+
+ private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(
+ Class inputType, GeneratedType typeSpec, SchemaNode node) {
+ try {
+ log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
+ val ctCls = createClass(typeSpec.codecClassName) [
+ //staticField(Map,"AUGMENTATION_SERIALIZERS");
+ staticQNameField(inputType);
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
+ staticField(it, AUGMENTATION_CODEC, BindingCodec)
+ implementsType(BINDING_CODEC)
method(Object, "toDomStatic", QName, Object) [
modifiers = PUBLIC + FINAL + STATIC
body = serializeBodyFacade(typeSpec, node)
]
method(Object, "serialize", Object) [
body = '''
- return toDomStatic(QNAME,$1);
+ {
+ java.util.Map.Entry _input = (java.util.Map.Entry) $1;
+ «QName.name» _localName = QNAME;
+ if(_input.getKey() != null) {
+ _localName = («QName.name») _input.getKey();
+ }
+ return toDomStatic(_localName,_input.getValue());
+ }
'''
]
method(Object, "fromDomStatic", QName, Object) [
]
]
- val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
- return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain) as Class<? extends BindingCodec<Map<QName,Object>, Object>>
+ listener?.onDataContainerCodecCreated(inputType, ret);
+ log.info("DOM Codec for {} was generated {}", inputType, ret)
+ return ret;
} catch (Exception e) {
- processException(inputType,e);
+ processException(inputType, e);
return null;
}
}
-
-
- private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(Class inputType,
- GeneratedType typeSpec, ChoiceNode node) {
+
+ private def Class<? extends BindingCodec<Map<QName, Object>, Object>> generateAugmentationTransformerFor(
+ Class inputType, GeneratedType type, AugmentationSchema node) {
try {
- log.info("Generating DOM Codec for {} with {}",inputType,inputType.classLoader)
- val ctCls = createClass(typeSpec.transformatorFqn) [
+ log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
+ val properties = type.allProperties
+ val ctCls = createClass(type.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
- //staticQNameField(inputType);
- implementsType(ctTransformator)
+ staticQNameField(inputType);
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
+ staticField(it, AUGMENTATION_CODEC, BindingCodec)
+ implementsType(BINDING_CODEC)
method(Object, "toDomStatic", QName, Object) [
modifiers = PUBLIC + FINAL + STATIC
body = '''
- return null;
+ {
+ //System.out.println("Qname " + $1);
+ //System.out.println("Value " + $2);
+ «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
+ java.util.List _childNodes = new java.util.ArrayList();
+ «type.resolvedName» value = («type.resolvedName») $2;
+ «FOR child : node.childNodes»
+ «var signature = properties.getFor(child)»
+ //System.out.println("«signature.key»" + value.«signature.key»());
+ «serializeProperty(child, signature.value, signature.key)»
+ «ENDFOR»
+ return ($r) _childNodes;
+ }
'''
]
method(Object, "serialize", Object) [
body = '''
- return null;
+ {
+ java.util.Map.Entry _input = (java.util.Map.Entry) $1;
+ «QName.name» _localName = QNAME;
+ if(_input.getKey() != null) {
+ _localName = («QName.name») _input.getKey();
+ }
+ return toDomStatic(_localName,_input.getValue());
+ }
'''
]
method(Object, "fromDomStatic", QName, Object) [
modifiers = PUBLIC + FINAL + STATIC
body = '''
- return null;
+ {
+ «QName.name» _localQName = QNAME;
+
+ if($2 == null) {
+ return null;
+ }
+ java.util.Map _compositeNode = (java.util.Map) $2;
+ ////System.out.println(_localQName + " " + _compositeNode);
+ «type.builderName» _builder = new «type.builderName»();
+ «FOR child : node.childNodes»
+ «val signature = properties.getFor(child)»
+ «deserializeProperty(child, signature.value, signature.key)»
+
+ _builder.«signature.key.toSetter»(«signature.key»);
+ «ENDFOR»
+ return _builder.build();
+ }
'''
]
method(Object, "deserialize", Object) [
body = '''
- return null;
+ return fromDomStatic(QNAME,$1);
'''
]
]
- val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
- return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain) as Class<? extends BindingCodec<Map<QName,Object>, Object>>
+ listener?.onDataContainerCodecCreated(inputType, ret);
+ return ret;
} catch (Exception e) {
- processException(inputType,e);
+ processException(inputType, e);
+ return null;
+ }
+ }
+
+ private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateTransformerFor(
+ Class inputType, GeneratedType typeSpec, ChoiceNode node) {
+ try {
+ log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
+ val ctCls = createClass(typeSpec.codecClassName) [
+ //staticField(Map,"AUGMENTATION_SERIALIZERS");
+ //staticQNameField(inputType);
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
+ staticField(it, CLASS_TO_CASE_MAP, Map)
+ staticField(it, COMPOSITE_TO_CASE, Map)
+ //staticField(it,QNAME_TO_CASE_MAP,BindingCodec)
+ implementsType(BINDING_CODEC)
+ method(List, "toDomStatic", QName, Object) [
+ modifiers = PUBLIC + FINAL + STATIC
+ body = '''
+ {
+ if($2 == null) {
+ return null;
+ }
+ «DataObject.name» _baValue = («DataObject.name») $2;
+ Class _baClass = _baValue.getImplementedInterface();
+ «BINDING_CODEC.name» _codec = «CLASS_TO_CASE_MAP».get(_baClass);
+ if(_codec == null) {
+ return null;
+ }
+ java.util.Map.Entry _input = new «SimpleEntry.name»($1,_baValue);
+ return (java.util.List) _codec.serialize(_input);
+ }
+ '''
+ ]
+ method(Object, "serialize", Object) [
+ body = '''
+ throw new «UnsupportedOperationException.name»("Direct invocation not supported.");
+ '''
+ ]
+ method(Object, "fromDomStatic", QName, Map) [
+ modifiers = PUBLIC + FINAL + STATIC
+ body = '''
+ {
+ «BINDING_CODEC.name» _codec = («BINDING_CODEC.name») «COMPOSITE_TO_CASE».get($2);
+ if(_codec != null) {
+ return _codec.deserialize(new «SimpleEntry.name»($1,$2));
+ }
+ return null;
+ }
+ '''
+ ]
+ method(Object, "deserialize", Object) [
+ body = '''
+ throw new «UnsupportedOperationException.name»("Direct invocation not supported.");
+ '''
+ ]
+ ]
+
+ val rawRet = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
+ val ret = rawRet as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
+ listener?.onChoiceCodecCreated(inputType, ret);
+ log.info("DOM Codec for {} was generated {}", inputType, ret)
+ return ret;
+ } catch (Exception e) {
+ processException(inputType, e);
return null;
}
}
-
private def keyConstructorList(List<QName> qnames) {
val names = new TreeSet<String>()
}
java.util.Map _compositeNode = (java.util.Map) $2;
«type.builderName» _builder = new «type.builderName»();
-
return _builder.build();
}
'''
«type.builderName» _builder = new «type.builderName»();
«deserializeKey(type, node)»
«deserializeDataNodeContainerBody(type, node)»
+ «deserializeAugmentations»
return _builder.build();
}
'''
java.util.Map _compositeNode = (java.util.Map) $2;
«type.builderName» _builder = new «type.builderName»();
«deserializeDataNodeContainerBody(type, node)»
+ «deserializeAugmentations»
return _builder.build();
}
'''
private def dispatch String deserializeBodyImpl(GeneratedType type, ChoiceCaseNode node) '''
{
«QName.name» _localQName = «QName.name».create($1,QNAME.getLocalName());
+
if($2 == null) {
return null;
}
java.util.Map _compositeNode = (java.util.Map) $2;
+ ////System.out.println(_localQName + " " + _compositeNode);
«type.builderName» _builder = new «type.builderName»();
«deserializeDataNodeContainerBody(type, node)»
+ «deserializeAugmentations»
return _builder.build();
}
'''
deserializeNodeContainerBodyImpl(type, type.allProperties, node);
}
- private def deserializeNodeContainerBodyImpl(GeneratedType type, HashMap<String, MethodSignature> properties,
+ private def deserializeNodeContainerBodyImpl(GeneratedType type, HashMap<String, Type> properties,
DataNodeContainer node) {
val ret = '''
«FOR child : node.childNodes.filter[!augmenting]»
«val signature = properties.getFor(child)»
- «deserializeProperty(child, signature.returnType, signature)»
- _builder.«signature.name.toSetter»(«signature.name»);
+ «deserializeProperty(child, signature.value, signature.key)»
+
+ _builder.«signature.key.toSetter»(«signature.key»);
«ENDFOR»
'''
return ret;
}
+ def deserializeAugmentations() '''
+ java.util.Map _augmentation = (java.util.Map) «AUGMENTATION_CODEC».deserialize(_compositeNode);
+ if(_augmentation != null) {
+ «Iterator.name» _entries = _augmentation.entrySet().iterator();
+ while(_entries.hasNext()) {
+ java.util.Map.Entry _entry = (java.util.Map.Entry) _entries.next();
+ //System.out.println("Aug. key:" + _entry.getKey());
+ Class _type = (Class) _entry.getKey();
+ «Augmentation.resolvedName» _value = («Augmentation.name») _entry.getValue();
+ _builder.addAugmentation(_type,_value);
+ }
+ }
+ '''
+
private def dispatch CharSequence deserializeProperty(ListSchemaNode schema, ParameterizedType type,
- MethodSignature property) '''
- java.util.List _dom_«property.name» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
+ String propertyName) '''
+ java.util.List _dom_«propertyName» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
localName»"));
- //System.out.println("«property.name»#deCode"+_dom_«property.name»);
- java.util.List «property.name» = new java.util.ArrayList();
- if(_dom_«property.name» != null) {
+ ////System.out.println("«propertyName»#deCode"+_dom_«propertyName»);
+ java.util.List «propertyName» = new java.util.ArrayList();
+ if(_dom_«propertyName» != null) {
java.util.List _serialized = new java.util.ArrayList();
- java.util.Iterator _iterator = _dom_«property.name».iterator();
+ java.util.Iterator _iterator = _dom_«propertyName».iterator();
boolean _hasNext = _iterator.hasNext();
while(_hasNext) {
Object _listItem = _iterator.next();
- //System.out.println(" item" + _listItem);
- Object _value = «type.actualTypeArguments.get(0).serializer.name».fromDomStatic(_localQName,_listItem);
- //System.out.println(" value" + _value);
- «property.name».add(_value);
+ ////System.out.println(" item" + _listItem);
+ Object _value = «type.actualTypeArguments.get(0).serializer.resolvedName».fromDomStatic(_localQName,_listItem);
+ ////System.out.println(" value" + _value);
+ «propertyName».add(_value);
_hasNext = _iterator.hasNext();
}
}
- //System.out.println(" list" + «property.name»);
+ ////System.out.println(" list" + «propertyName»);
'''
private def dispatch CharSequence deserializeProperty(LeafListSchemaNode schema, ParameterizedType type,
- MethodSignature property) '''
- java.util.List _dom_«property.name» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
+ String propertyName) '''
+ java.util.List _dom_«propertyName» = _compositeNode.get(«QName.name».create(_localQName,"«schema.QName.
localName»"));
- java.util.List «property.name» = new java.util.ArrayList();
- if(_dom_«property.name» != null) {
+ java.util.List «propertyName» = new java.util.ArrayList();
+ if(_dom_«propertyName» != null) {
java.util.List _serialized = new java.util.ArrayList();
- java.util.Iterator _iterator = _dom_«property.name».iterator();
+ java.util.Iterator _iterator = _dom_«propertyName».iterator();
boolean _hasNext = _iterator.hasNext();
while(_hasNext) {
Object _listItem = _iterator.next();
if(_listItem instanceof java.util.Map.Entry) {
Object _innerValue = ((java.util.Map.Entry) _listItem).getValue();
Object _value = «deserializeValue(type.actualTypeArguments.get(0), "_innerValue")»;
- «property.name».add(_value);
+ «propertyName».add(_value);
}
_hasNext = _iterator.hasNext();
}
}
'''
- private def dispatch CharSequence deserializeProperty(LeafSchemaNode schema, Type type, MethodSignature property) '''
- java.util.List _dom_«property.name»_list =
+ private def dispatch CharSequence deserializeProperty(LeafSchemaNode schema, Type type, String propertyName) '''
+ java.util.List _dom_«propertyName»_list =
_compositeNode.get(«QName.name».create(_localQName,"«schema.QName.localName»"));
- «type.resolvedName» «property.name» = null;
- if(_dom_«property.name»_list != null && _dom_«property.name»_list.size() > 0) {
- java.util.Map.Entry _dom_«property.name» = (java.util.Map.Entry) _dom_«property.name»_list.get(0);
- Object _inner_value = _dom_«property.name».getValue();
- «property.name» = «deserializeValue(type, "_inner_value")»;
+ «type.resolvedName» «propertyName» = null;
+ if(_dom_«propertyName»_list != null && _dom_«propertyName»_list.size() > 0) {
+ java.util.Map.Entry _dom_«propertyName» = (java.util.Map.Entry) _dom_«propertyName»_list.get(0);
+ Object _inner_value = _dom_«propertyName».getValue();
+ «propertyName» = «deserializeValue(type, "_inner_value")»;
}
'''
private def dispatch CharSequence deserializeProperty(ContainerSchemaNode schema, Type type,
- MethodSignature property) '''
- java.util.List _dom_«property.name»_list =
+ String propertyName) '''
+ java.util.List _dom_«propertyName»_list =
_compositeNode.get(«QName.name».create(_localQName,"«schema.QName.localName»"));
- «type.resolvedName» «property.name» = null;
- if(_dom_«property.name»_list != null && _dom_«property.name»_list.size() > 0) {
+ «type.resolvedName» «propertyName» = null;
+ if(_dom_«propertyName»_list != null && _dom_«propertyName»_list.size() > 0) {
- java.util.Map _dom_«property.name» = (java.util.Map) _dom_«property.name»_list.get(0);
- «type.resolvedName» «property.name» = «type.serializer.name».fromDomStatic(_localQName,_dom_«property.name»);
+ java.util.Map _dom_«propertyName» = (java.util.Map) _dom_«propertyName»_list.get(0);
+ «propertyName» = «type.serializer.resolvedName».fromDomStatic(_localQName,_dom_«propertyName»);
}
'''
+ private def dispatch CharSequence deserializeProperty(ChoiceNode schema, Type type, String propertyName) '''
+ «type.resolvedName» «propertyName» = «type.serializer.resolvedName».fromDomStatic(_localQName,_compositeNode);
+ '''
+
private def dispatch String deserializeValue(GeneratedTransferObject type, String domParameter) '''
- («type.resolvedName») «type.valueSerializer.name».fromDomValue(«domParameter»);
+ («type.resolvedName») «type.valueSerializer.resolvedName».fromDomValue(«domParameter»);
'''
private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateValueTransformer(
val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
}
- val ctCls = createClass(typeSpec.transformatorFqn) [
+ val ctCls = createClass(typeSpec.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
- implementsType(ctTransformator)
+ implementsType(BINDING_CODEC)
+ staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
implementsType(BindingDeserializer.asCtClass)
method(Object, "toDomValue", Object) [
modifiers = PUBLIC + FINAL + STATIC
return null;
}
«typeSpec.resolvedName» _encapsulatedValue = («typeSpec.resolvedName») $1;
- //System.out.println("«inputType.simpleName»#toDomValue:Enc: "+_encapsulatedValue);
+ ////System.out.println("«inputType.simpleName»#toDomValue:Enc: "+_encapsulatedValue);
«returnType.resolvedName» _value = _encapsulatedValue.getValue();
- //System.out.println("«inputType.simpleName»#toDomValue:DeEnc: "+_value);
- return _value;
+ ////System.out.println("«inputType.simpleName»#toDomValue:DeEnc: "+_value);
+ Object _domValue = «serializeValue(returnType, "_value")»;
+ return _domValue;
}
'''
]
modifiers = PUBLIC + FINAL + STATIC
body = '''
{
- //System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
+ ////System.out.println("«inputType.simpleName»#fromDomValue: "+$1);
if($1 == null) {
return null;
]
val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
- log.info("DOM Codec for {} was generated {}",inputType,ret)
+ log.info("DOM Codec for {} was generated {}", inputType, ret)
return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
} catch (Exception e) {
- log.error("Cannot compile DOM Codec for {}",inputType,e);
+ log.error("Cannot compile DOM Codec for {}", inputType, e);
val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
exception.addSuppressed(e);
throw exception;
}
private def createDummyImplementation(Class<?> object, GeneratedTransferObject typeSpec) {
- log.info("Generating Dummy DOM Codec for {} with {}",object,object.classLoader)
- return createClass(typeSpec.transformatorFqn) [
+ log.info("Generating Dummy DOM Codec for {} with {}", object, object.classLoader)
+ return createClass(typeSpec.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
- implementsType(ctTransformator)
+ implementsType(BINDING_CODEC)
implementsType(BindingDeserializer.asCtClass)
method(Object, "toDomValue", Object) [
modifiers = PUBLIC + FINAL + STATIC
private def dispatch Class<? extends BindingCodec<Map<QName, Object>, Object>> generateValueTransformer(
Class<?> inputType, Enumeration typeSpec) {
try {
- log.info("Generating DOM Codec for {} with {}",inputType,inputType.classLoader)
- val ctCls = createClass(typeSpec.transformatorFqn) [
+ log.info("Generating DOM Codec for {} with {}", inputType, inputType.classLoader)
+ val ctCls = createClass(typeSpec.codecClassName) [
//staticField(Map,"AUGMENTATION_SERIALIZERS");
- implementsType(ctTransformator)
+ implementsType(BINDING_CODEC)
method(Object, "toDomValue", Object) [
modifiers = PUBLIC + FINAL + STATIC
body = '''
]
val ret = ctCls.toClassImpl(inputType.classLoader, inputType.protectionDomain)
- log.info("DOM Codec for {} was generated {}",inputType,ret)
+ log.info("DOM Codec for {} was generated {}", inputType, ret)
return ret as Class<? extends BindingCodec<Map<QName,Object>, Object>>;
} catch (CodeGenerationException e) {
- throw new CodeGenerationException("Cannot compile Transformator for " + inputType,e);
+ throw new CodeGenerationException("Cannot compile Transformator for " + inputType, e);
} catch (Exception e) {
- log.error("Cannot compile DOM Codec for {}",inputType,e);
+ log.error("Cannot compile DOM Codec for {}", inputType, e);
val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType);
exception.addSuppressed(e);
throw exception;
}
}
-
+
def Class<?> toClassImpl(CtClass newClass, ClassLoader loader, ProtectionDomain domain) {
- val cls = newClass.toClass(loader,domain);
- if(classFileCapturePath !== null) {
+ val cls = newClass.toClass(loader, domain);
+ if (classFileCapturePath !== null) {
newClass.writeFile(classFileCapturePath.absolutePath);
}
+ listener?.onCodecCreated(cls);
return cls;
}
-
+
def debugWriteClass(CtClass class1) {
- val path = class1.name.replace(".","/")+".class"
-
- val captureFile = new File(classFileCapturePath,path);
+ val path = class1.name.replace(".", "/") + ".class"
+
+ val captureFile = new File(classFileCapturePath, path);
captureFile.createNewFile
-
-
+
}
- private def dispatch String deserializeValue(Type type, String domParameter) '''(«type.resolvedName») «domParameter»'''
+ private def dispatch String deserializeValue(Type type, String domParameter) {
+ if (INSTANCE_IDENTIFIER.equals(type)) {
+
+ return '''(«InstanceIdentifier.name») «INSTANCE_IDENTIFIER_CODEC».deserialize(«domParameter»)'''
+ }
+
+ return '''(«type.resolvedName») «domParameter»'''
+
+ }
/**
* Default catch all
*
**/
- private def dispatch CharSequence deserializeProperty(DataSchemaNode container, Type type, MethodSignature property) '''
- «type.resolvedName» «property.name» = null;
+ private def dispatch CharSequence deserializeProperty(DataSchemaNode container, Type type, String propertyName) '''
+ «type.resolvedName» «propertyName» = null;
'''
private def dispatch CharSequence deserializeProperty(DataSchemaNode container, GeneratedTypeBuilder type,
- MethodSignature property) {
- _deserializeProperty(container, type.toInstance, property)
+ String propertyName) {
+ _deserializeProperty(container, type.toInstance, propertyName)
}
public static def toSetter(String it) {
}
/*
- private def dispatch CharSequence deserializeProperty(DataSchemaNode container,GeneratedType type, MethodSignature property) '''
- «property.returnType.resolvedName» «property.name» = value.«property.name»();
- if(«property.name» != null) {
- Object domValue = «type.serializer».toDomStatic(QNAME,«property.name»);
- childNodes.add(domValue);
+ private def dispatch CharSequence deserializeProperty(DataSchemaNode container,GeneratedType type, String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
+ if(«propertyName» != null) {
+ Object domValue = «type.serializer».toDomStatic(QNAME,«propertyName»);
+ _childNodes.add(domValue);
}
'''
*/
private def getBuilderName(GeneratedType type) '''«type.resolvedName»Builder'''
- private def staticQNameField(CtClass it, Class node) {
+ private def staticQNameField(CtClass it, Class<?> node) {
val field = new CtField(ctQName, "QNAME", it);
field.modifiers = PUBLIC + FINAL + STATIC;
addField(field, '''«node.name».QNAME''')
}
-
+
private def staticQNameField(CtClass it, QName node) {
val field = new CtField(ctQName, "QNAME", it);
field.modifiers = PUBLIC + FINAL + STATIC;
- addField(field, '''«QName.asCtClass.name».create("«node.namespace»","«node.formattedRevision»","«node.localName»")''')
+ addField(field,
+ '''«QName.asCtClass.name».create("«node.namespace»","«node.formattedRevision»","«node.localName»")''')
}
private def dispatch String serializeBody(GeneratedType type, ListSchemaNode node) '''
{
- «QName.name» resultName = «QName.name».create($1,QNAME.getLocalName());
- java.util.List childNodes = new java.util.ArrayList();
+ «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
+ java.util.List _childNodes = new java.util.ArrayList();
«type.resolvedName» value = («type.resolvedName») $2;
«transformDataContainerBody(type.allProperties, node)»
- return ($r) java.util.Collections.singletonMap(resultName,childNodes);
+ «serializeAugmentations»
+ return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
}
'''
private def dispatch String serializeBody(GeneratedType type, ContainerSchemaNode node) '''
{
- «QName.name» resultName = «QName.name».create($1,QNAME.getLocalName());
- java.util.List childNodes = new java.util.ArrayList();
+ «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
+ java.util.List _childNodes = new java.util.ArrayList();
«type.resolvedName» value = («type.resolvedName») $2;
«transformDataContainerBody(type.allProperties, node)»
- return ($r) java.util.Collections.singletonMap(resultName,childNodes);
+ «serializeAugmentations»
+ return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
}
'''
private def dispatch String serializeBody(GeneratedType type, ChoiceCaseNode node) '''
{
- «QName.name» resultName = «QName.name».create($1,QNAME.getLocalName());
- java.util.List childNodes = new java.util.ArrayList();
+ «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
+ java.util.List _childNodes = new java.util.ArrayList();
«type.resolvedName» value = («type.resolvedName») $2;
«transformDataContainerBody(type.allProperties, node)»
- return ($r) java.util.Collections.singletonMap(resultName,childNodes);
+ «serializeAugmentations»
+ return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
}
'''
private def dispatch String serializeBody(GeneratedType type, SchemaNode node) '''
{
- «QName.name» resultName = «QName.name».create($1,QNAME.getLocalName());
- java.util.List childNodes = new java.util.ArrayList();
+ «QName.name» _resultName = «QName.name».create($1,QNAME.getLocalName());
+ java.util.List _childNodes = new java.util.ArrayList();
«type.resolvedName» value = («type.resolvedName») $2;
- return ($r) java.util.Collections.singletonMap(resultName,childNodes);
+ return ($r) java.util.Collections.singletonMap(_resultName,_childNodes);
}
'''
-
- private def transformDataContainerBody(Map<String, MethodSignature> properties, DataNodeContainer node) {
+ private def transformDataContainerBody(Map<String, Type> properties, DataNodeContainer node) {
val ret = '''
«FOR child : node.childNodes.filter[!augmenting]»
«var signature = properties.getFor(child)»
- «serializeProperty(child, signature.returnType, signature)»
+ //System.out.println("«signature.key»" + value.«signature.key»());
+ «serializeProperty(child, signature.value, signature.key)»
«ENDFOR»
'''
return ret;
}
-
- def MethodSignature getFor(Map<String,MethodSignature> map, DataSchemaNode node) {
+
+ def serializeAugmentations() '''
+ java.util.List _augmentations = (java.util.List) «AUGMENTATION_CODEC».serialize(value);
+ if(_augmentations != null) {
+ _childNodes.addAll(_augmentations);
+ }
+ '''
+
+ def Entry<String, Type> getFor(Map<String, Type> map, DataSchemaNode node) {
val sig = map.get(node.getterName);
- if(sig == null) {
- return map.get(node.booleanGetterName);
+ if (sig == null) {
+
+ return new SimpleEntry(node.booleanGetterName, map.get(node.booleanGetterName));
}
- return sig;
+ return new SimpleEntry(node.getterName, sig);
}
private static def String getBooleanGetterName(DataSchemaNode node) {
}
private def dispatch CharSequence serializeProperty(ListSchemaNode schema, ParameterizedType type,
- MethodSignature property) '''
- «property.returnType.resolvedName» «property.name» = value.«property.name»();
- if(«property.name» != null) {
- java.util.Iterator _iterator = «property.name».iterator();
+ String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
+ if(«propertyName» != null) {
+ java.util.Iterator _iterator = «propertyName».iterator();
boolean _hasNext = _iterator.hasNext();
while(_hasNext) {
Object _listItem = _iterator.next();
- Object _domValue = «type.actualTypeArguments.get(0).serializer.name».toDomStatic(QNAME,_listItem);
- childNodes.add(_domValue);
+ Object _domValue = «type.actualTypeArguments.get(0).serializer.resolvedName».toDomStatic(_resultName,_listItem);
+ _childNodes.add(_domValue);
_hasNext = _iterator.hasNext();
}
}
'''
- private def dispatch CharSequence serializeProperty(LeafSchemaNode schema, Type type, MethodSignature property) '''
- «property.returnType.resolvedName» «property.name» = value.«property.name»();
+ private def dispatch CharSequence serializeProperty(LeafSchemaNode schema, Type type, String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
- if(«property.name» != null) {
- «QName.name» _qname = «QName.name».create(resultName,"«schema.QName.localName»");
- Object _propValue = «serializeValue(type, property.name)»;
+ if(«propertyName» != null) {
+ «QName.name» _qname = «QName.name».create(_resultName,"«schema.QName.localName»");
+ Object _propValue = «serializeValue(type, propertyName)»;
if(_propValue != null) {
Object _domValue = java.util.Collections.singletonMap(_qname,_propValue);
- childNodes.add(_domValue);
+ _childNodes.add(_domValue);
}
}
'''
- private def dispatch serializeValue(GeneratedTransferObject type, String parameter) '''«type.valueSerializer.name».toDomValue(«parameter»)'''
+ private def dispatch serializeValue(GeneratedTransferObject type, String parameter) '''«type.valueSerializer.
+ resolvedName».toDomValue(«parameter»)'''
- private def dispatch serializeValue(Type signature, String property) '''«property»'''
+ private def dispatch serializeValue(Type signature, String property) {
+ if (INSTANCE_IDENTIFIER == signature) {
+ return '''«INSTANCE_IDENTIFIER_CODEC».serialize(«property»)'''
+ }
+ return '''«property»''';
+ }
- private def dispatch CharSequence serializeProperty(LeafListSchemaNode schema, Type type, MethodSignature property) '''
- «property.returnType.resolvedName» «property.name» = value.«property.name»();
- if(«property.name» != null) {
- «QName.name» _qname = «QName.name».create(resultName,"«schema.QName.localName»");
- java.util.Iterator _iterator = «property.name».iterator();
+ private def dispatch CharSequence serializeProperty(LeafListSchemaNode schema, ParameterizedType type,
+ String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
+ if(«propertyName» != null) {
+ «QName.name» _qname = «QName.name».create(_resultName,"«schema.QName.localName»");
+ java.util.Iterator _iterator = «propertyName».iterator();
boolean _hasNext = _iterator.hasNext();
while(_hasNext) {
Object _listItem = _iterator.next();
- Object _propValue = «property.name»;
+ Object _propValue = «serializeValue(type.actualTypeArguments.get(0), "_listItem")»;
Object _domValue = java.util.Collections.singletonMap(_qname,_propValue);
- childNodes.add(_domValue);
+ _childNodes.add(_domValue);
_hasNext = _iterator.hasNext();
}
}
'''
+ private def dispatch CharSequence serializeProperty(ChoiceNode container, GeneratedType type,
+ String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
+ if(«propertyName» != null) {
+ java.util.List domValue = «type.serializer.resolvedName».toDomStatic(_resultName,«propertyName»);
+ _childNodes.addAll(domValue);
+ }
+ '''
+
/**
* Default catch all
*
**/
- private def dispatch CharSequence serializeProperty(DataSchemaNode container, Type type, MethodSignature property) '''
- «property.returnType.resolvedName» «property.name» = value.«property.name»();
- if(«property.name» != null) {
- Object domValue = «property.name»;
- childNodes.add(domValue);
+ private def dispatch CharSequence serializeProperty(DataSchemaNode container, Type type, String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
+ if(«propertyName» != null) {
+ Object domValue = «propertyName»;
+ _childNodes.add(domValue);
}
'''
private def dispatch CharSequence serializeProperty(DataSchemaNode container, GeneratedTypeBuilder type,
- MethodSignature property) {
- serializeProperty(container, type.toInstance, property)
+ String propertyName) {
+ serializeProperty(container, type.toInstance, propertyName)
}
private def dispatch CharSequence serializeProperty(DataSchemaNode container, GeneratedType type,
- MethodSignature property) '''
- «property.returnType.resolvedName» «property.name» = value.«property.name»();
- if(«property.name» != null) {
- Object domValue = «type.serializer.name».toDomStatic(QNAME,«property.name»);
- childNodes.add(domValue);
+ String propertyName) '''
+ «type.resolvedName» «propertyName» = value.«propertyName»();
+ if(«propertyName» != null) {
+ Object domValue = «type.serializer.resolvedName».toDomStatic(_resultName,«propertyName»);
+ _childNodes.add(domValue);
}
'''
-
-
- private def transformatorFqn(GeneratedType typeSpec) {
+ private def codecClassName(GeneratedType typeSpec) {
return '''«typeSpec.resolvedName»$Broker$Codec$DOM'''
}
- private def transformatorFqn(Class typeSpec) {
+ private def codecClassName(Class typeSpec) {
return '''«typeSpec.name»$Broker$Codec$DOM'''
}
- private def HashMap<String, MethodSignature> getAllProperties(GeneratedType type) {
- val ret = new HashMap<String, MethodSignature>();
+ private def dispatch HashMap<String, Type> getAllProperties(GeneratedType type) {
+ val ret = new HashMap<String, Type>();
type.collectAllProperties(ret);
return ret;
}
- private def dispatch void collectAllProperties(GeneratedType type, Map<String, MethodSignature> set) {
+ private def dispatch void collectAllProperties(GeneratedType type, Map<String, Type> set) {
for (definition : type.methodDefinitions) {
- set.put(definition.name, definition);
+ set.put(definition.name, definition.returnType);
+ }
+ for (property : type.properties) {
+ set.put(property.getterName, property.returnType);
}
-
for (parent : type.implements) {
parent.collectAllProperties(set);
}
}
- private def dispatch void collectAllProperties(Type type, Map<String, MethodSignature> set) {
+ def String getGetterName(GeneratedProperty property) {
+ return "get" + property.name.toFirstUpper
+ }
+
+ private def dispatch void collectAllProperties(Type type, Map<String, Type> set) {
// NOOP for generic type.
}
return type.asCtClass.name;
}
+ def String getResolvedName(Class type) {
+ return type.asCtClass.name;
+ }
+
def CtClass asCtClass(Type type) {
val name = type.fullyQualifiedName
val cls = loadClassWithTCCL(type.fullyQualifiedName)
return cls.asCtClass;
}
-
-
-
- private def dispatch processException(Class<?> inputType,CodeGenerationException e){
- log.error("Cannot compile DOM Codec for {}. One of it's prerequisites was not generated.",inputType);
+
+ private def dispatch processException(Class<?> inputType, CodeGenerationException e) {
+ log.error("Cannot compile DOM Codec for {}. One of it's prerequisites was not generated.", inputType);
throw e;
}
-
- private def dispatch processException(Class<?> inputType,Exception e){
- log.error("Cannot compile DOM Codec for {}",inputType,e);
- val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType,e);
+
+ private def dispatch processException(Class<?> inputType, Exception e) {
+ log.error("Cannot compile DOM Codec for {}", inputType, e);
+ val exception = new CodeGenerationException("Cannot compile Transformator for " + inputType, e);
throw exception;
}
Type type;
@Property
- MethodSignature signature;
+ Type returnType;
@Property
SchemaNode schemaNode;
}
try {
listener.onNotification(notification);
} catch (Exception e) {
- log.error("Unhandled exception {} thrown by listener: {} Notification: {}", e, listener, notification);
+ log.error("Unhandled exception thrown by listener: {}", listener, e);
}
return null;
}
import java.util.Map.Entry;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(InstanceIdentifier<? extends DataObject> path);
DataObject dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode result);
+
+
}
+++ /dev/null
-package org.opendaylight.controller.sal.binding.impl.connect.dom
-
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext
-import java.util.List
-import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
-import org.opendaylight.yangtools.sal.binding.model.api.Type
-import org.opendaylight.yangtools.yang.model.api.SchemaNode
-import java.util.Map
-import org.opendaylight.yangtools.yang.model.api.SchemaPath
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
-import org.opendaylight.yangtools.binding.generator.util.Types
-import java.util.HashMap
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.binding.DataContainer
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
-import java.util.Collections
-import java.util.ArrayList
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
-import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition
-import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition
-import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
-import org.opendaylight.yangtools.yang.model.util.ExtendedType
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
-import com.google.common.collect.FluentIterable
-import org.opendaylight.yangtools.yang.data.api.SimpleNode
-import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
-import org.opendaylight.controller.sal.binding.impl.util.ClassLoaderUtils
-import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition
-import com.google.common.collect.HashMultimap
-import com.google.common.collect.ArrayListMultimap
-import com.google.common.collect.Multimap
-import java.util.Collection
-import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
-
-class BindingMapping {
-
- @Property
- val Map<Type, GeneratedTypeBuilder> typeToDefinition = new HashMap();
-
- @Property
- val Map<Type, SchemaNode> typeToSchemaNode = new HashMap();
-
- def QName getSchemaNode(Class<?> cls) {
- val ref = Types.typeForClass(cls);
- return typeToSchemaNode.get(ref)?.QName;
- }
-
- def void updateBinding(SchemaContext schemaContext, ModuleContext moduleBindingContext) {
- updateBindingFor(moduleBindingContext.childNodes, schemaContext);
-
- }
-
- def org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
- InstanceIdentifier<? extends DataObject> obj) {
- val pathArguments = obj.path;
- var Class<? extends DataObject> parent;
- val dataDomArgs = new ArrayList<PathArgument>();
- for (pathArgument : pathArguments) {
- dataDomArgs.add(pathArgument.toDataDomPathArgument(parent));
- parent = pathArgument.type;
- }
-
- return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(dataDomArgs);
- }
-
-
-
- def DataObject dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> identifier, CompositeNode node) {
- if (node == null) {
- return null;
- }
- val targetClass = identifier.targetType;
- val classLoader = targetClass.classLoader;
- val ref = Types.typeForClass(targetClass);
- val targetType = typeToDefinition.get(ref);
- val targetSchema = typeToSchemaNode.get(ref);
- return node.toDataObject(classLoader, targetType.toInstance, targetSchema);
-
- }
-
- private def dispatch PathArgument toDataDomPathArgument(IdentifiableItem argument, Class<? extends DataObject> parent) {
- val Class rawType = argument.type;
- val ref = Types.typeForClass(rawType);
- val schemaType = typeToSchemaNode.get(ref);
- val qname = schemaType.QName
-
- val Object key = argument.key;
- val predicates = key.toPredicates(schemaType as ListSchemaNode);
-
- return new NodeIdentifierWithPredicates(qname, predicates);
- }
-
- private def dispatch PathArgument toDataDomPathArgument(Item<?> argument, Class<? extends DataObject> parent) {
- val ref = Types.typeForClass(argument.type);
- val qname = typeToSchemaNode.get(ref).QName
- return new NodeIdentifier(qname);
- }
-
- private def Map<QName, Object> toPredicates(Object identifier, ListSchemaNode node) {
- val keyDefinitions = node.keyDefinition;
- val map = new HashMap<QName, Object>();
- for (keydef : keyDefinitions) {
- val keyNode = node.getDataChildByName(keydef) as LeafSchemaNode;
- val value = identifier.getSimpleValue(keydef, keyNode.type);
- map.put(keydef, value.value);
- }
- return map;
- }
-
- def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
- for (entry : map.entrySet) {
- val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
- typeToDefinition.put(entry.value, entry.value);
- typeToSchemaNode.put(entry.value, schemaNode)
- }
- }
-
- def CompositeNode toCompositeNode(DataContainer data) {
- val type = data.implementedInterface;
- val typeRef = Types.typeForClass(type);
- val schemaNode = typeToSchemaNode.get(typeRef);
- val generatedType = typeToDefinition.get(typeRef);
-
- return data.toDataDom(schemaNode, generatedType);
- }
-
- private def dispatch CompositeNode toDataDom(DataContainer data, ContainerSchemaNode node,
- GeneratedTypeBuilder builder) {
- val subnodes = data.toDataDomComponents(node);
- return new CompositeNodeTOImpl(node.QName, null, subnodes);
- }
-
- private def dispatch CompositeNode toDataDom(DataContainer data, NotificationDefinition node,
- GeneratedTypeBuilder builder) {
- val subnodes = data.toDataDomComponents(node);
- return new CompositeNodeTOImpl(node.QName, null, subnodes);
- }
-
- private def dispatch CompositeNode toDataDom(DataContainer data, ListSchemaNode node,
- GeneratedTypeBuilder builder) {
- val subnodes = data.toDataDomComponents(node);
- return new CompositeNodeTOImpl(node.QName, null, subnodes);
- }
-
- private def List<Node<?>> toDataDomComponents(DataContainer data, DataNodeContainer node) {
- val subnodes = new ArrayList<Node<?>>();
- for (childNode : node.childNodes) {
- val value = childNode.dataDomFromParent(data);
- if (value !== null) {
- subnodes.addAll(value);
- }
- }
- return subnodes;
- }
-
- private def List<Node<?>> dataDomFromParent(DataSchemaNode node, DataContainer container) {
- if (node.augmenting) {
- return Collections.emptyList();
- }
- return dataDomFromParentImpl(node, container);
- }
-
- private def dispatch List<Node<?>> dataDomFromParentImpl(LeafSchemaNode node, DataContainer container) {
- val value = container.getSimpleValue(node.QName, node.type);
- if (value !== null) {
- return Collections.<Node<?>>singletonList(value);
- }
- return Collections.emptyList();
- }
-
- private def dispatch List<Node<?>> dataDomFromParentImpl(LeafListSchemaNode node, DataContainer container) {
- val values = container.getSimpleValues(node);
- if (values !== null) {
- //val it = new ArrayList<Node<?>>();
- //for (value : values) {
- //}
-
- }
- return Collections.emptyList();
- }
-
- private def getSimpleValues(DataContainer container, LeafListSchemaNode node) {
- return Collections.emptyList();
- }
-
- private def dispatch List<Node<?>> dataDomFromParentImpl(ListSchemaNode node, DataContainer container) {
- val qname = node.QName;
- val values = container.<List>getValue(qname, List) as List<? extends DataContainer>;
- if (values === null) {
- return Collections.emptyList;
- }
- val it = new ArrayList<Node<?>>();
- for (value : values) {
- add(value.toCompositeNode());
- }
-
- return it;
- }
-
- private def dispatch List<Node<?>> dataDomFromParentImpl(ChoiceNode node, DataContainer container) {
- }
-
- private def dispatch List<Node<?>> serializeValueImpl(List<?> list, GeneratedTypeBuilder builder,
- ListSchemaNode node) {
- val it = new ArrayList<Node<?>>();
- for (value : list) {
-
- val serVal = value.serializeValueImpl(builder, node);
- if (serVal !== null) {
- addAll(serVal);
- }
- }
- return it;
- }
-
- public static def dispatch Node<?> getSimpleValue(Object container, QName name, ExtendedType type) {
- getSimpleValue(container, name, type.baseType);
- }
-
- public static def dispatch Node<?> getSimpleValue(Object container, QName name, StringTypeDefinition type) {
- val value = container.getValue(name, String);
- if(value === null) return null;
- return new SimpleNodeTOImpl(name, null, value);
- }
-
- public static def dispatch Node<?> getSimpleValue(Object container, QName name, TypeDefinition<?> type) {
- val value = container.getValue(name, Object);
- if(value === null) return null;
- return new SimpleNodeTOImpl(name, null, value);
- }
-
- public static def dispatch Node<?> getSimpleValue(Object container, QName name, BooleanTypeDefinition type) {
- val value = container.getValue(name, Boolean);
- if(value === null) return null;
- return new SimpleNodeTOImpl(name, null, value);
- }
-
- public static def dispatch Node<?> getSimpleValue(Object container, QName name, BinaryTypeDefinition type) {
- val Object value = container.getValue(name, Object); //Constants.BYTES_CLASS);
- if(value === null) return null;
- return new SimpleNodeTOImpl(name, null, value);
- }
-
- public static def <T> T getValue(Object object, QName node, Class<T> type) {
- val methodName = BindingGeneratorImpl.getterMethodName(node.localName, Types.typeForClass(type));
- var clz = object.class;
- if (object instanceof DataContainer) {
- clz = (object as DataContainer).implementedInterface;
- }
- val method = clz.getMethod(methodName);
- if (method === null) {
- return null;
- }
- val value = method.invoke(object);
- if (value === null) {
- return null;
- }
- if (type.isAssignableFrom(value.class)) {
- return value as T;
- }
- return value.getEncapsulatedValue(type);
- }
-
- public static def <T> T getEncapsulatedValue(Object value, Class<T> type) {
- val method = value.class.getMethod("getValue");
- if (method !== null && type.isAssignableFrom(method.returnType)) {
- return method.invoke(value) as T;
- }
- return null;
- }
-
- private def dispatch List<Node<?>> serializeValueImpl(DataContainer data, GeneratedTypeBuilder builder,
- SchemaNode node) {
- return Collections.<Node<?>>singletonList(data.toDataDom(node, builder));
- }
-
- private def dispatch List<Node<?>> serializeValueImpl(Object object, GeneratedTypeBuilder builder,
- SchemaNode node) {
- }
-
- def DataObject toDataObject(CompositeNode node, ClassLoader loader, GeneratedType type, SchemaNode schema) {
-
- // Nasty reflection hack (for now)
- val builderClass = loader.loadClass(type.builderFQN);
- val builder = builderClass.newInstance;
- val buildMethod = builderClass.getMethod("build");
-
- node.fillDataObject(builder, loader, type, schema);
-
- return buildMethod.invoke(builder) as DataObject;
- }
-
- private def dispatch void fillDataObject(CompositeNode node, Object builder, ClassLoader loader, GeneratedType type,
- ListSchemaNode schema) {
-
- if (schema.keyDefinition !== null && !schema.keyDefinition.empty) {
-
- val value = node.keyToBindingKey(loader, type, schema);
- builder.setProperty("key", value);
- }
- node.fillBuilderFromContainer(builder,loader,type,schema);
- }
-
-
-
- private def dispatch void fillDataObject(CompositeNode node, Object builder, ClassLoader loader, GeneratedType type,
- ContainerSchemaNode schema) {
- node.fillBuilderFromContainer(builder,loader,type,schema);
- }
-
-
- private def void fillBuilderFromContainer(CompositeNode node, Object builder, ClassLoader loader, GeneratedType type, DataNodeContainer schema) {
- val Multimap<QName,Node<?>> dataMap = ArrayListMultimap.create();
- for(child :node.children) {
- dataMap.put(child.nodeType,node);
- }
- for(entry : dataMap.asMap.entrySet) {
- val entrySchema = schema.getDataChildByName(entry.key);
- val entryType = type.methodDefinitions.byQName(entry.key);
- entry.value.addValueToBuilder(builder,loader,entryType,entrySchema);
- }
- }
-
- private def Type byQName(List<MethodSignature> signatures, QName name) {
-
- }
-
- private def dispatch addValueToBuilder(Collection<Node<? extends Object>> nodes, Object object, ClassLoader loader, Object object2, LeafSchemaNode container) {
-
- }
-
-
-
- private def dispatch addValueToBuilder(Collection<Node<? extends Object>> nodes, Object object, ClassLoader loader, Object object2, ContainerSchemaNode container) {
-
- }
-
-
- private def dispatch addValueToBuilder(Collection<Node<? extends Object>> nodes, Object object, ClassLoader loader, Object object2, ListSchemaNode container) {
-
- }
-
- private def dispatch addValueToBuilder(Collection<Node<? extends Object>> nodes, Object object, ClassLoader loader, Object object2, LeafListSchemaNode container) {
-
- }
-
-
-
-
- private def Object keyToBindingKey(CompositeNode node, ClassLoader loader, GeneratedType type, ListSchemaNode schema) {
- val keyClass = loader.loadClass(type.keyFQN);
- val constructor = keyClass.constructors.get(0);
- val keyType = type.keyTypeProperties;
- val args = new ArrayList();
- for (key : schema.keyDefinition) {
- var keyProperty = keyType.get(BindingGeneratorUtil.parseToClassName(key.localName));
- if (keyProperty == null) {
- keyProperty = keyType.get(BindingGeneratorUtil.parseToValidParamName(key.localName));
- }
- val domKeyValue = node.getFirstSimpleByName(key);
- val keyValue = domKeyValue.deserializeSimpleValue(loader, keyProperty.returnType,
- schema.getDataChildByName(key));
- args.add(keyValue);
- }
- return ClassLoaderUtils.construct(constructor, args);
- }
-
- private def dispatch Object deserializeSimpleValue(SimpleNode<? extends Object> node, ClassLoader loader, Type type,
- LeafSchemaNode node2) {
- deserializeSimpleValueImpl(node, loader, type, node2.type);
- }
-
- private def dispatch Object deserializeSimpleValue(SimpleNode<? extends Object> node, ClassLoader loader, Type type,
- LeafListSchemaNode node2) {
- deserializeSimpleValueImpl(node, loader, type, node2.type);
- }
-
- private def dispatch Object deserializeSimpleValueImpl(SimpleNode<? extends Object> node, ClassLoader loader, Type type,
- ExtendedType definition) {
- deserializeSimpleValueImpl(node, loader, type, definition.baseType);
- }
-
- private def dispatch Object deserializeSimpleValueImpl(SimpleNode<? extends Object> node, ClassLoader loader, Type type,
- StringTypeDefinition definition) {
- if (type instanceof GeneratedTransferObject) {
- val cls = loader.getClassForType(type);
- val const = cls.getConstructor(String);
- val str = String.valueOf(node.value);
- return const.newInstance(str);
- }
- return node.value;
- }
-
- private def Class<?> getClassForType(ClassLoader loader, Type type) {
- loader.loadClass(type.fullyQualifiedName);
- }
-
- private def dispatch Object deserializeSimpleValueImpl(SimpleNode<? extends Object> node, ClassLoader loader, Type type,
- TypeDefinition definition) {
- throw new UnsupportedOperationException("TODO: auto-generated method stub")
- }
-
- private def Map<String, GeneratedProperty> getKeyTypeProperties(GeneratedType type) {
- val method = FluentIterable.from(type.methodDefinitions).findFirst[name == "getKey"]
- val key = method.returnType as GeneratedTransferObject;
- val ret = new HashMap<String, GeneratedProperty>();
- for (prop : key.properties) {
- ret.put(prop.name, prop);
- }
- return ret;
- }
-
- private def void setProperty(Object object, String property, Object value) {
- val cls = object.class;
- val valMethod = cls.getMethod("set" + property.toFirstUpper, value.class);
- if (valMethod != null)
- valMethod.invoke(object, value);
- }
-
- private def String getBuilderFQN(Type type) '''«type.fullyQualifiedName»Builder'''
-
- private def String getKeyFQN(Type type) '''«type.fullyQualifiedName»Key'''
-
-}
-
-@Data
-class PropertyCapture {
-
- @Property
- val Type returnType;
- @Property
- val String name;
-
-}
import javassist.ClassPool;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl;
import org.opendaylight.controller.sal.binding.dom.serializer.impl.TransformerGenerator;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.controller.sal.core.api.Provider;
public void onSessionInitiated(ProviderSession session) {
RuntimeGeneratedMappingServiceImpl mappingImpl = new RuntimeGeneratedMappingServiceImpl();
+ mappingImpl.setPool(new ClassPool());
SchemaService schemaService = (session.getService(SchemaService.class));
ClassPool pool = new ClassPool();
mappingImpl.setBinding(new TransformerGenerator(pool));
+++ /dev/null
-package org.opendaylight.controller.sal.binding.impl.connect.dom
-
-import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.sal.binding.model.api.CodeGenerator
-import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl
-import org.opendaylight.yangtools.sal.binding.generator.api.BindingGenerator
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-import java.util.Collections
-import java.util.Map.Entry
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import java.util.AbstractMap.SimpleEntry
-import org.opendaylight.controller.sal.core.api.model.SchemaService
-
-class MappingServiceImpl implements SchemaServiceListener, BindingIndependentMappingService {
-
- var extension BindingMapping mapping = new BindingMapping;
-
- @Property
- BindingGeneratorImpl binding;
-
- @Property
- SchemaService schemaService;
-
- override onGlobalContextUpdated(SchemaContext arg0) {
- recreateBindingContext(arg0);
- }
-
- def recreateBindingContext(SchemaContext schemaContext) {
- val newBinding = new BindingGeneratorImpl();
- newBinding.generateTypes(schemaContext);
- val newMapping = new BindingMapping();
- for (entry : newBinding.moduleContexts.entrySet) {
- val module = entry.key;
- val context = entry.value;
-
- newMapping.updateBinding(schemaContext, context);
- }
- mapping = newMapping
- }
-
- override CompositeNode toDataDom(DataObject data) {
- mapping.toCompositeNode(data);
- }
-
- override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
- Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
- val key = mapping.toDataDom(entry.key);
- val data = mapping.toCompositeNode(entry.value);
- return new SimpleEntry(key, data);
- }
-
- override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
- InstanceIdentifier<? extends DataObject> path) {
- return mapping.toDataDom(path);
- }
-
- override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode result) {
- return mapping.dataObjectFromDataDom(path,result);
- }
-
- public def void start() {
- schemaService.registerSchemaServiceListener(this);
- recreateBindingContext(schemaService.globalContext);
- }
-}
+++ /dev/null
-package org.opendaylight.controller.sal.binding.impl.connect.dom
-
-import org.opendaylight.controller.sal.binding.dom.serializer.impl.TransformerGenerator
-import javassist.ClassPool
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
-import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl
-import java.util.Map
-import org.opendaylight.yangtools.sal.binding.model.api.Type
-import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder
-import org.opendaylight.yangtools.yang.model.api.SchemaNode
-import java.util.HashMap
-import java.util.concurrent.ConcurrentHashMap
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import java.util.Map.Entry
-import java.util.AbstractMap.SimpleEntry
-import org.opendaylight.yangtools.yang.model.api.SchemaPath
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
-import java.util.ArrayList
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
-import org.opendaylight.yangtools.binding.generator.util.Types
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier
-import org.opendaylight.yangtools.yang.binding.DataContainer
-import static com.google.common.base.Preconditions.*;
-import java.util.List
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
-import org.opendaylight.yangtools.concepts.Delegator
-import java.util.concurrent.ConcurrentMap
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
-import org.opendaylight.yangtools.yang.binding.BindingCodec
-
-class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaServiceListener {
-
- ClassPool pool;
-
- @Property
- extension TransformerGenerator binding;
-
- val ConcurrentMap<Type, Type> typeDefinitions = new ConcurrentHashMap();
-
- val ConcurrentMap<Class<? extends DataContainer>, TransformerWrapper> domSerializers = new ConcurrentHashMap();
-
- @Property
- val ConcurrentMap<Type, GeneratedTypeBuilder> typeToDefinition = new ConcurrentHashMap();
-
- @Property
- val ConcurrentMap<Type, SchemaNode> typeToSchemaNode = new ConcurrentHashMap();
-
- override onGlobalContextUpdated(SchemaContext arg0) {
- recreateBindingContext(arg0);
- }
-
- def recreateBindingContext(SchemaContext schemaContext) {
- val newBinding = new BindingGeneratorImpl();
- newBinding.generateTypes(schemaContext);
-
- for (entry : newBinding.moduleContexts.entrySet) {
-
- //val module = entry.key;
- val context = entry.value;
- updateBindingFor(context.childNodes, schemaContext);
-
- val typedefs = context.typedefs;
- for(typedef : typedefs.values) {
- binding.typeDefinitions.put(typedef,typedef as GeneratedType);
- }
- }
- }
-
- override CompositeNode toDataDom(DataObject data) {
- toCompositeNodeImpl(data);
- }
-
- override Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> toDataDom(
- Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry) {
- val key = toDataDomImpl(entry.key);
- val data = toCompositeNodeImpl(entry.value);
- return new SimpleEntry(key, data);
- }
-
- private def CompositeNode toCompositeNodeImpl(DataObject object) {
- val cls = object.implementedInterface;
- val transformator = resolveTransformator(cls);
- val ret = transformator.transform(object);
- return ret;
- }
-
- private def resolveTransformator(Class<? extends DataContainer> cls) {
- val serializer = domSerializers.get(cls);
- if (serializer !== null) {
- return serializer;
- }
- val transformerClass = binding.transformerFor(cls).newInstance;
- val wrapper = new TransformerWrapper(transformerClass);
- domSerializers.putIfAbsent(cls, wrapper);
- return wrapper;
- }
-
- private def org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDomImpl(
- InstanceIdentifier<? extends DataObject> object) {
- val pathArguments = object.path;
- var Class<? extends DataObject> parent;
- val dataDomArgs = new ArrayList<PathArgument>();
- for (pathArgument : pathArguments) {
- dataDomArgs.add(pathArgument.toDataDomPathArgument(parent));
- parent = pathArgument.type;
- }
-
- return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(dataDomArgs);
- }
-
- override org.opendaylight.yangtools.yang.data.api.InstanceIdentifier toDataDom(
- InstanceIdentifier<? extends DataObject> path) {
- return toDataDomImpl(path);
- }
-
- override dataObjectFromDataDom(InstanceIdentifier<? extends DataObject> path, CompositeNode result) {
- return dataObjectFromDataDomImpl(path, result);
- }
-
- def DataObject dataObjectFromDataDomImpl(InstanceIdentifier<? extends DataObject> identifier, CompositeNode node) {
- val targetType = identifier.targetType
- val transformer = resolveTransformator(targetType);
- val ret = transformer.deserialize(node) as DataObject;
- return ret;
- }
-
- def void updateBindingFor(Map<SchemaPath, GeneratedTypeBuilder> map, SchemaContext module) {
- for (entry : map.entrySet) {
- val schemaNode = SchemaContextUtil.findDataSchemaNode(module, entry.key);
- typeToDefinition.put(entry.value, entry.value);
- typeToSchemaNode.put(entry.value, schemaNode)
- }
- }
-
- private def dispatch PathArgument toDataDomPathArgument(IdentifiableItem argument,
- Class<? extends DataObject> parent) {
- val Class<?> rawType = argument.type;
- val ref = Types.typeForClass(rawType);
- val schemaType = typeToSchemaNode.get(ref);
- val qname = schemaType.QName
-
- val Object key = argument.key;
- val predicates = key.toPredicates(schemaType as ListSchemaNode);
-
- return new NodeIdentifierWithPredicates(qname, predicates);
- }
-
- private def Map<QName, Object> toPredicates(Object identifier, ListSchemaNode node) {
- val keyDefinitions = node.keyDefinition;
- val map = new HashMap<QName, Object>();
- for (keydef : keyDefinitions) {
- val keyNode = node.getDataChildByName(keydef) as LeafSchemaNode;
- val value = BindingMapping.getSimpleValue(identifier, keydef, keyNode.type);
- map.put(keydef, value.value);
- }
- return map;
- }
-
- private def dispatch PathArgument toDataDomPathArgument(Item<?> argument, Class<? extends DataObject> parent) {
- val ref = Types.typeForClass(argument.type);
- val qname = typeToSchemaNode.get(ref).QName
- return new NodeIdentifier(qname);
- }
-
- public def void start() {
- pool = new ClassPool()
- binding = new TransformerGenerator(pool);
-
- binding.typeToDefinition = typeToDefinition
- binding.typeToSchemaNode = typeToSchemaNode
- binding.typeDefinitions = typeDefinitions
-
- }
-}
-
-class TransformerWrapper implements // //
-Delegator<BindingCodec<Map<QName, Object>, Object>> {
-
- @Property
- val BindingCodec<Map<QName, Object>, Object> delegate;
-
- new(BindingCodec<Map<QName, Object>, Object> delegate) {
- _delegate = delegate;
- }
-
- def CompositeNode transform(DataObject input) {
- val ret = delegate.serialize(input);
- val node = toNode(ret)
- return node as CompositeNode;
- }
-
- def deserialize(CompositeNode node) {
- if (node === null) {
- return null;
- }
- val Map mapCapture = node
- return delegate.deserialize(mapCapture as Map<QName,Object>);
- }
-
- static def Node<?> toNode(Map map) {
- val nodeMap = map as Map<QName,Object>;
- checkArgument(map.size == 1);
- val elem = nodeMap.entrySet.iterator.next;
- val qname = elem.key;
- val value = elem.value;
- toNodeImpl(qname, value);
- }
-
- static def dispatch Node<?> toNodeImpl(QName name, List objects) {
- val values = new ArrayList<Node<?>>(objects.size);
- for (obj : objects) {
- values.add(toNode(obj as Map));
- }
- return new CompositeNodeTOImpl(name, null, values);
- }
-
- static def dispatch Node<?> toNodeImpl(QName name, Map<QName, Object> object) {
- throw new UnsupportedOperationException("Unsupported node hierarchy.");
- }
-
- static def dispatch Node<?> toNodeImpl(QName name, Object object) {
- return new SimpleNodeTOImpl(name, null, object);
- }
-}
import java.util.concurrent.Callable;
+import java.util.concurrent.locks.Lock;
import static com.google.common.base.Preconditions.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import com.google.common.base.Optional;
+
public class ClassLoaderUtils {
public static <V> V withClassLoader(ClassLoader cls,Callable<V> function) throws Exception {
- checkNotNull(cls);
- checkNotNull(function);
+ return withClassLoaderAndLock(cls, Optional.<Lock>absent(), function);
+ }
+
+ public static <V> V withClassLoaderAndLock(ClassLoader cls,Lock lock,Callable<V> function) throws Exception {
+ checkNotNull(lock,"Lock should not be null");
+ return withClassLoaderAndLock(cls, Optional.of(lock), function);
+ }
+
+ public static <V> V withClassLoaderAndLock(ClassLoader cls,Optional<Lock> lock,Callable<V> function) throws Exception {
+ checkNotNull(cls, "Classloader should not be null");
+ checkNotNull(function,"Function should not be null");
+ if(lock.isPresent()) {
+ lock.get().lock();
+ }
ClassLoader oldCls = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(cls);
V result = function.call();
- Thread.currentThread().setContextClassLoader(oldCls);
return result;
- } catch (Exception e) {
+ } finally {
Thread.currentThread().setContextClassLoader(oldCls);
- throw new Exception(e);
+ if(lock.isPresent()) {
+ lock.get().unlock();
+ }
}
}
import java.util.List;
import java.util.Set;
+import javassist.ClassPool;
+
import org.junit.Before;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl;
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentDataServiceConnector;
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService;
-import org.opendaylight.controller.sal.binding.impl.connect.dom.RuntimeGeneratedMappingServiceImpl;
-import org.opendaylight.controller.sal.binding.test.connect.dom.MappingServiceTest;
+import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl;
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore;
import org.opendaylight.yangtools.yang.model.api.Module;
protected DataBrokerService biDataService;
protected DataProviderService baDataService;
+ /**
+ * Workaround for JUNIT sharing classloaders
+ *
+ */
+ protected static final ClassPool POOL = new ClassPool();
+
protected RuntimeGeneratedMappingServiceImpl mappingServiceImpl;
protected BindingIndependentMappingService mappingService;
protected DataBrokerImpl baDataImpl;
biDataImpl.registerCommitHandler(treeRoot, dataStore);
mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl();
+ mappingServiceImpl.setPool(POOL);
mappingService = mappingServiceImpl;
File pathname = new File("target/gen-classes-debug");
//System.out.println("Generated classes are captured in " + pathname.getAbsolutePath());
connectorServiceImpl.start();
String[] yangFiles= getModelFilenames();
- mappingServiceImpl.onGlobalContextUpdated(getContext(yangFiles));
+ if(yangFiles != null && yangFiles.length > 0) {
+ mappingServiceImpl.onGlobalContextUpdated(getContext(yangFiles));
+ }
}
protected String[] getModelFilenames() {
- return getModelFilenamesImpl();
+ return getAllModelFilenames();
}
- public static String[] getModelFilenamesImpl() {
+ public static String[] getAllModelFilenames() {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean apply(String input) {
package org.opendaylight.controller.sal.binding.test.bugfix;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopMplsAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopMplsActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
import static org.junit.Assert.*;
-/**
- *
- * Testcase for https://bugs.opendaylight.org/show_bug.cgi?id=144
- *
- * Cannot compile CoDec for org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.Flow
- *
- * @author ttkacik
- *
- */
public class DOMCodecBug01Test extends AbstractDataServiceTest {
private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
private static final QName FLOW_ID_QNAME = QName.create(Flow.QNAME, "id");
private static final QName FLOW_NODE_QNAME = QName.create(Flow.QNAME, "node");
-
- private static final String FLOW_ID = "foo";
+ private static final long FLOW_ID = 1234;
private static final String NODE_ID = "node:1";
private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
- private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder().node(Nodes.class)
- .node(Node.class, NODE_KEY).toInstance();
-
-
+
private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
NODE_ID);
-
+
+ private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder() //
+ .node(Nodes.class) //
+ .toInstance();
+
+ private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODES_INSTANCE_ID_BI = //
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Nodes.QNAME) //
+ .toInstance();
+
+ private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder() //
+ .node(Nodes.class) //
+ .child(Node.class, NODE_KEY).toInstance();
+
private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODE_INSTANCE_ID_BI = //
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
- .node(Nodes.QNAME) //
- .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
- .toInstance();
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Nodes.QNAME) //
+ .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .toInstance();
private static final NodeRef NODE_REF = new NodeRef(NODE_INSTANCE_ID_BA);
-
+
private static final FlowKey FLOW_KEY = new FlowKey(FLOW_ID, NODE_REF);
private static final Map<QName, Object> FLOW_KEY_BI = //
- ImmutableMap.<QName,Object>of(FLOW_ID_QNAME, FLOW_ID, FLOW_NODE_QNAME, NODE_REF);
-
-
-
+ ImmutableMap.<QName, Object> of(FLOW_ID_QNAME, FLOW_ID, FLOW_NODE_QNAME, NODE_INSTANCE_ID_BI);
private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier FLOW_INSTANCE_ID_BI = //
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
- .node(Flows.QNAME) //
- .nodeWithKey(Flow.QNAME, FLOW_KEY_BI) //
- .toInstance();
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Flows.QNAME) //
+ .nodeWithKey(Flow.QNAME, FLOW_KEY_BI) //
+ .toInstance();
private static final InstanceIdentifier<? extends DataObject> FLOW_INSTANCE_ID_BA = //
- InstanceIdentifier.builder() //
- .node(Flows.class) //
- .node(Flow.class, FLOW_KEY) //
- .toInstance();
+ InstanceIdentifier.builder() //
+ .node(Flows.class) //
+ .node(Flow.class, FLOW_KEY) //
+ .toInstance();
+
+
+
/**
- *
+ *
+ * Testcase for https://bugs.opendaylight.org/show_bug.cgi?id=
+ *
+ * Cannot compile CoDec for
+ * org.opendaylight.yang.gen.v1.urn.opendaylight.flow
+ * .config.rev130819.flows.Flow
+ *
* When invoking following code in the consumer, user got an
* IllegalStateException during creation of mapping between Java DTOs and
* data-dom.
- *
+ *
* Exception was compilation error which was caused by incorect generation
* of code.
- *
- *
+ *
+ * Reported by Depthi V V
+ *
*/
@Test
public void testIndirectGeneration() throws Exception {
+ ExecutorService basePool = Executors.newFixedThreadPool(2);
+ ListeningExecutorService listenablePool = MoreExecutors.listeningDecorator(basePool);
+
+ createFlow();
+
+ Object lock = new Object();
+ CreateFlowTask task1 = new CreateFlowTask(lock);
+ CreateFlowTask task2 = new CreateFlowTask(lock);
+ CreateFlowTask task3 = new CreateFlowTask(lock);
+
+ ListenableFuture<Void> task1Future = listenablePool.submit(task1);
+ ListenableFuture<Void> task2Future = listenablePool.submit(task2);
+ ListenableFuture<Void> task3Future = listenablePool.submit(task3);
+
+
+ ListenableFuture<List<Void>> compositeFuture = Futures.allAsList(task1Future,task2Future,task3Future);
+
+ Thread.sleep(500);
+ //lock.notifyAll();
+ compositeFuture.get();
+
+ verifyDataAreStoredProperly();
+
+ DataModificationTransaction modification2 = baDataService.beginTransaction();
+ modification2.removeConfigurationData(FLOW_INSTANCE_ID_BA);
+
+ DataObject originalData = modification2.getOriginalConfigurationData().get(FLOW_INSTANCE_ID_BA);
+ assertNotNull(originalData);
+ RpcResult<TransactionStatus> ret2 = modification2.commit().get();
+
+ assertNotNull(ret2);
+ assertEquals(TransactionStatus.COMMITED, ret2.getResult());
+
+ // Data are not in the store.
+ assertNull(baDataService.readOperationalData(FLOW_INSTANCE_ID_BA));
+
+ }
+
+ private void createFlow() throws Exception {
+
DataModificationTransaction modification = baDataService.beginTransaction();
- FlowBuilder flow = new FlowBuilder();
+ FlowBuilder flow = new FlowBuilder();
MatchBuilder match = new MatchBuilder();
VlanMatchBuilder vlanBuilder = new VlanMatchBuilder();
VlanIdBuilder vlanIdBuilder = new VlanIdBuilder();
flow.setMatch(match.build());
flow.setNode(NODE_REF);
+
+ InstructionsBuilder instructions = new InstructionsBuilder();
+ InstructionBuilder instruction = new InstructionBuilder();
+ ApplyActionsBuilder applyActions = new ApplyActionsBuilder();
+ List<Action> actionList = new ArrayList<>();
+ PopMplsActionBuilder popMplsAction = new PopMplsActionBuilder();
+ popMplsAction.setEthernetType(34);
+ actionList.add(new ActionBuilder().setAction(popMplsAction.build()).build());
+
+ applyActions.setAction(actionList );
+
+
+ instruction.setInstruction(applyActions.build());
+
+
+ List<Instruction> instructionList = Collections.<Instruction>singletonList(instruction.build());
+ instructions.setInstruction(instructionList );
+
+ flow.setInstructions(instructions.build());
modification.putConfigurationData(FLOW_INSTANCE_ID_BA, flow.build());
RpcResult<TransactionStatus> ret = modification.commit().get();
assertNotNull(ret);
assertEquals(TransactionStatus.COMMITED, ret.getResult());
-
- verifyDataAreStoredProperly();
-
-
- DataModificationTransaction modification2 = baDataService.beginTransaction();
- modification2.removeConfigurationData(FLOW_INSTANCE_ID_BA);
-
- DataObject originalData = modification2.getOriginalConfigurationData().get(FLOW_INSTANCE_ID_BA);
- assertNotNull(originalData);
- RpcResult<TransactionStatus> ret2 = modification2.commit().get();
-
- assertNotNull(ret2);
- assertEquals(TransactionStatus.COMMITED, ret2.getResult());
-
-
- // Data are not in the store.
- assertNull(baDataService.readOperationalData(FLOW_INSTANCE_ID_BA));
-
-
+ }
+
+ private class CreateFlowTask implements Callable<Void> {
+
+ final Object startSyncObject;
+
+ public CreateFlowTask(Object startSync) {
+ startSyncObject = startSync;
+ }
+
+ @Override
+ public Void call() {
+ try {
+ //startSyncObject.wait();
+ //Thread.sleep(500);
+ createFlow();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
}
private void verifyDataAreStoredProperly() {
CompositeNode biFlow = biDataService.readConfigurationData(FLOW_INSTANCE_ID_BI);
assertNotNull(biFlow);
- CompositeNode biMatch = biFlow.getFirstCompositeByName(QName.create(Flow.QNAME,Match.QNAME.getLocalName()));
+ CompositeNode biMatch = biFlow.getFirstCompositeByName(QName.create(Flow.QNAME, Match.QNAME.getLocalName()));
assertNotNull(biMatch);
}
+
+
}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.bugfix;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import static org.junit.Assert.*;
+
+public class DOMCodecBug02Test extends AbstractDataServiceTest {
+
+ private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
+ private static final QName FLOW_ID_QNAME = QName.create(Flow.QNAME, "id");
+ private static final QName FLOW_NODE_QNAME = QName.create(Flow.QNAME, "node");
+
+ private static final String FLOW_ID = "foo";
+ private static final String NODE_ID = "node:1";
+
+ private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+
+ private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
+ NODE_ID);
+
+ private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder() //
+ .node(Nodes.class) //
+ .toInstance();
+
+ private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODES_INSTANCE_ID_BI = //
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Nodes.QNAME) //
+ .toInstance();
+
+ private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder() //
+ .node(Nodes.class) //
+ .child(Node.class, NODE_KEY).toInstance();
+
+ private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODE_INSTANCE_ID_BI = //
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Nodes.QNAME) //
+ .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .toInstance();
+ private static final NodeRef NODE_REF = new NodeRef(NODE_INSTANCE_ID_BA);
+
+ @Override
+ protected String[] getModelFilenames() {
+ return null;
+ }
+
+ /**
+ *
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSchemaContextNotAvailable() throws Exception {
+
+ ExecutorService testExecutor = Executors.newFixedThreadPool(1);
+
+ Future<Future<RpcResult<TransactionStatus>>> future = testExecutor.submit(new Callable<Future<RpcResult<TransactionStatus>>>() {
+ @Override
+ public Future<RpcResult<TransactionStatus>> call() throws Exception {
+ NodesBuilder nodesBuilder = new NodesBuilder();
+ nodesBuilder.setNode(Collections.<Node> emptyList());
+ DataModificationTransaction transaction = baDataService.beginTransaction();
+ transaction.putOperationalData(NODES_INSTANCE_ID_BA, nodesBuilder.build());
+ return transaction.commit();
+ }
+ });
+ mappingServiceImpl.onGlobalContextUpdated(getContext(getAllModelFilenames()));
+
+ RpcResult<TransactionStatus> result = future.get().get();
+ assertEquals(TransactionStatus.COMMITED, result.getResult());
+
+
+ Nodes nodes = checkForNodes();
+ assertNotNull(nodes);
+
+ }
+
+ private Nodes checkForNodes() {
+ return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.test.bugfix;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import static org.junit.Assert.*;
+
+public class DOMCodecBug03Test extends AbstractDataServiceTest implements DataChangeListener {
+
+ private static final QName NODE_ID_QNAME = QName.create(Node.QNAME, "id");
+ private static final String NODE_ID = "openflow:1";
+
+ private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+
+ private static final Map<QName, Object> NODE_KEY_BI = Collections.<QName, Object> singletonMap(NODE_ID_QNAME,
+ NODE_ID);
+
+ private static final InstanceIdentifier<Nodes> NODES_INSTANCE_ID_BA = InstanceIdentifier.builder() //
+ .node(Nodes.class) //
+ .toInstance();
+
+ private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODES_INSTANCE_ID_BI = //
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Nodes.QNAME) //
+ .toInstance();
+
+ private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder() //
+ .node(Nodes.class) //
+ .child(Node.class, NODE_KEY).toInstance();
+
+ private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier NODE_INSTANCE_ID_BI = //
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder() //
+ .node(Nodes.QNAME) //
+ .nodeWithKey(Node.QNAME, NODE_KEY_BI) //
+ .toInstance();
+ private static final NodeRef NODE_REF = new NodeRef(NODE_INSTANCE_ID_BA);
+ private DataChangeEvent<InstanceIdentifier<?>, DataObject> receivedChangeEvent;
+
+
+
+ /**
+ * Test for Bug 148
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testAugmentSerialization() throws Exception {
+
+
+ baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this);
+
+ NodeBuilder nodeBuilder = new NodeBuilder();
+ nodeBuilder.setId(new NodeId(NODE_ID));
+ nodeBuilder.setKey(NODE_KEY);
+ DataModificationTransaction transaction = baDataService.beginTransaction();
+
+
+ FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
+ fnub.setHardware("Hardware Foo");
+ fnub.setManufacturer("Manufacturer Foo");
+ fnub.setSerialNumber("Serial Foo");
+ fnub.setDescription("Description Foo");
+ fnub.setSoftware("JUnit emulated");
+ FlowCapableNode fnu = fnub.build();
+ nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
+ Node original = nodeBuilder.build();
+ transaction.putOperationalData(NODE_INSTANCE_ID_BA, original);
+
+ RpcResult<TransactionStatus> result = transaction.commit().get();
+ assertEquals(TransactionStatus.COMMITED, result.getResult());
+
+ assertNotNull(receivedChangeEvent);
+
+ verifyNodes((Nodes) receivedChangeEvent.getUpdatedOperationalSubtree(),original);
+ assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
+ Nodes nodes = checkForNodes();
+ verifyNodes(nodes,original);
+
+
+ }
+
+ private void verifyNodes(Nodes nodes,Node original) {
+ assertNotNull(nodes);
+ assertNotNull(nodes.getNode());
+ assertEquals(1, nodes.getNode().size());
+ Node readedNode = nodes.getNode().get(0);
+ assertEquals(original.getId(), readedNode.getId());
+ assertEquals(original.getKey(), readedNode.getKey());
+
+ FlowCapableNode fnu = original.getAugmentation(FlowCapableNode.class);
+ FlowCapableNode readedAugment = readedNode.getAugmentation(FlowCapableNode.class);
+ assertNotNull(fnu);
+ assertEquals(fnu.getDescription(), readedAugment.getDescription());
+ assertEquals(fnu.getSerialNumber(), readedAugment.getSerialNumber());
+
+ }
+
+ private void assertBindingIndependentVersion(
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier nodeId) {
+ CompositeNode node = biDataService.readOperationalData(nodeId);
+ assertNotNull(node);
+ }
+
+ private Nodes checkForNodes() {
+ return (Nodes) baDataService.readOperationalData(NODES_INSTANCE_ID_BA);
+ }
+
+ @Override
+ public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ receivedChangeEvent = change;
+ }
+
+}
+++ /dev/null
-package org.opendaylight.controller.sal.binding.test.connect.dom;
-
-import static org.junit.Assert.*;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService;
-import org.opendaylight.controller.sal.binding.impl.connect.dom.MappingServiceImpl;
-import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-
-public class MappingServiceTest {
-
- private static final QName NODES = QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes");
- private static final QName NODE = QName.create(NODES,"node");
- private static final QName ID = QName.create(NODES,"id");
-
- BindingIndependentMappingService service;
- private MappingServiceImpl impl;
-
- @Before
- public void setUp() {
- impl = new MappingServiceImpl();
- service = impl;
- }
-
- @Test
- public void baDataToBiData() throws Exception {
-
- String[] yangFiles = AbstractDataServiceTest.getModelFilenamesImpl();
-
- SchemaContext ctx = AbstractDataServiceTest.getContext(yangFiles);
-
- impl.onGlobalContextUpdated(ctx);
-
- NodesBuilder nodes = new NodesBuilder();
-
- List<Node> nodeList = new ArrayList<>();
- nodeList.add(createChildNode("foo"));
- nodeList.add(createChildNode("bar"));
-
- nodes.setNode(nodeList);
- Nodes nodesTO = nodes.build();
- CompositeNode xmlNodes = service.toDataDom(nodesTO);
- assertNotNull(xmlNodes);
- List<CompositeNode> invNodes = xmlNodes.getCompositesByName(NODE);
- assertNotNull(invNodes);
- assertEquals(2, invNodes.size());
- }
-
- @Test
- public void instanceIdentifierTest() throws Exception {
-
- String[] yangFiles = AbstractDataServiceTest.getModelFilenamesImpl();
- SchemaContext ctx = AbstractDataServiceTest.getContext(yangFiles);
- impl.onGlobalContextUpdated(ctx);
-
- NodeKey nodeKey = new NodeKey(new NodeId("foo"));
- InstanceIdentifier<Node> path = InstanceIdentifier.builder().node(Nodes.class).child(Node.class, nodeKey).toInstance();
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier result = service.toDataDom(path);
- assertNotNull(result);
- assertEquals(2, result.getPath().size());
- }
-
- private Node createChildNode(String id) {
- NodeBuilder node = new NodeBuilder();
- NodeId nodeId = new NodeId(id);
-
- node.setId(nodeId);
- node.setKey(new NodeKey(nodeId));
-
- FlowCapableNodeBuilder aug = new FlowCapableNodeBuilder();
- aug.setManufacturer(id);
- node.addAugmentation(FlowCapableNode.class, aug.build());
-
- return node.build();
- }
-
-}
base "config:service-type";
config:java-class "org.opendaylight.controller.sal.binding.api.BindingAwareBroker";
}
+
+ identity binding-data-broker {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.sal.binding.api.data.DataProviderService";
+ }
+
+ identity binding-rpc-registry {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.sal.binding.api.RpcProviderRegistry";
+ }
+
+ identity binding-notification-service {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.sal.binding.api.NotificationProviderService";
+ }
+
}
\ No newline at end of file
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAddedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
import org.opendaylight.yangtools.concepts.Registration;
}
+ @Override
+ public void onNodeErrorNotification(NodeErrorNotification notification) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onNodeExperimenterErrorNotification(
+ NodeExperimenterErrorNotification notification) {
+ // TODO Auto-generated method stub
+
+ }
+
}
}
import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter
import org.opendaylight.yangtools.concepts.Path
import org.slf4j.LoggerFactory
-
-abstract class AbstractDataBroker<P extends Path<P>,D,DCL extends DataChangeListener<P,D>> implements
-DataModificationTransactionFactory<P, D>, //
+import java.util.HashSet
+import java.util.Map.Entry
+import java.util.Iterator
+import java.util.Collection
+import com.google.common.collect.FluentIterable;
+import java.util.Set
+import com.google.common.collect.ImmutableList
+
+abstract class AbstractDataBroker<P extends Path<P>, D, DCL extends DataChangeListener<P, D>> implements DataModificationTransactionFactory<P, D>, //
DataReader<P, D>, //
DataChangePublisher<P, D, DCL>, //
-DataProvisionService<P,D> {
+DataProvisionService<P, D> {
@Property
var ExecutorService executor;
@Property
- var AbstractDataReadRouter<P,D> dataReadRouter;
-
- Multimap<P, DataChangeListenerRegistration<P,D,DCL>> listeners = HashMultimap.create();
- Multimap<P, DataCommitHandlerRegistration<P,D>> commitHandlers = HashMultimap.create();
+ var AbstractDataReadRouter<P, D> dataReadRouter;
+ Multimap<P, DataChangeListenerRegistration<P, D, DCL>> listeners = HashMultimap.create();
+ Multimap<P, DataCommitHandlerRegistration<P, D>> commitHandlers = HashMultimap.create();
public new() {
-
+ }
+
+ protected def /*Iterator<Entry<Collection<DataChangeListenerRegistration<P,D,DCL>>,D>>*/ affectedCommitHandlers(
+ HashSet<P> paths) {
+ return FluentIterable.from(commitHandlers.asMap.entrySet)
+ .filter[key.isAffectedBy(paths)] //
+ .transformAndConcat [value] //
+ .transform[instance].toList()
}
override final readConfigurationData(P path) {
return dataReadRouter.readOperationalData(path);
}
- override final registerCommitHandler(P path,
- DataCommitHandler<P, D> commitHandler) {
- val registration = new DataCommitHandlerRegistration(path,commitHandler,this);
- commitHandlers.put(path,registration)
- return registration;
+ override final registerCommitHandler(P path, DataCommitHandler<P, D> commitHandler) {
+ val registration = new DataCommitHandlerRegistration(path, commitHandler, this);
+ commitHandlers.put(path, registration)
+ return registration;
}
override final def registerDataChangeListener(P path, DCL listener) {
return reg;
}
- final def registerDataReader(P path,DataReader<P,D> reader) {
-
- val confReg = dataReadRouter.registerConfigurationReader(path,reader);
- val dataReg = dataReadRouter.registerOperationalReader(path,reader);
-
- return new CompositeObjectRegistration(reader,Arrays.asList(confReg,dataReg));
+ final def registerDataReader(P path, DataReader<P, D> reader) {
+
+ val confReg = dataReadRouter.registerConfigurationReader(path, reader);
+ val dataReg = dataReadRouter.registerOperationalReader(path, reader);
+
+ return new CompositeObjectRegistration(reader, Arrays.asList(confReg, dataReg));
}
- protected final def removeListener(DataChangeListenerRegistration<P,D,DCL> registration) {
+ protected final def removeListener(DataChangeListenerRegistration<P, D, DCL> registration) {
listeners.remove(registration.path, registration);
}
- protected final def removeCommitHandler(DataCommitHandlerRegistration<P,D> registration) {
+ protected final def removeCommitHandler(DataCommitHandlerRegistration<P, D> registration) {
commitHandlers.remove(registration.path, registration);
}
-
- protected final def getActiveCommitHandlers() {
- return commitHandlers.entries.map[ value.instance].toSet
+
+ protected final def getActiveCommitHandlers() {
+ return commitHandlers.entries;
}
- package final def Future<RpcResult<TransactionStatus>> commit(AbstractDataTransaction<P,D> transaction) {
+ protected def /*Iterator<Entry<Collection<DataChangeListenerRegistration<P,D,DCL>>,D>>*/ affectedListenersWithInitialState(
+ HashSet<P> paths) {
+ return FluentIterable.from(listeners.asMap.entrySet).filter[key.isAffectedBy(paths)].transform [
+ val operationalState = readOperationalData(key)
+ val configurationState = readConfigurationData(key)
+ return new ListenerStateCapture(key, value, operationalState, configurationState)
+ ].toList()
+ }
+
+ protected def boolean isAffectedBy(P key, Set<P> paths) {
+ if (paths.contains(key)) {
+ return true;
+ }
+ for (path : paths) {
+ if (key.contains(path)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ package final def Future<RpcResult<TransactionStatus>> commit(AbstractDataTransaction<P, D> transaction) {
checkNotNull(transaction);
transaction.changeStatus(TransactionStatus.SUBMITED);
val task = new TwoPhaseCommit(transaction, this);
}
-package class DataChangeListenerRegistration<P extends Path<P>,D,DCL extends DataChangeListener<P,D>> extends AbstractObjectRegistration<DCL> implements ListenerRegistration<DCL> {
+@Data
+package class ListenerStateCapture<P extends Path<P>, D,DCL extends DataChangeListener<P, D>> {
- AbstractDataBroker<P,D,DCL> dataBroker;
+ @Property
+ P path;
+
+ @Property
+ Collection<DataChangeListenerRegistration<P, D, DCL>> listeners;
+
+ @Property
+ D initialOperationalState;
+
+ @Property
+ D initialConfigurationState;
+}
+
+package class DataChangeListenerRegistration<P extends Path<P>, D, DCL extends DataChangeListener<P, D>> extends AbstractObjectRegistration<DCL> implements ListenerRegistration<DCL> {
+
+ AbstractDataBroker<P, D, DCL> dataBroker;
@Property
val P path;
- new(P path, DCL instance, AbstractDataBroker<P,D,DCL> broker) {
+ new(P path, DCL instance, AbstractDataBroker<P, D, DCL> broker) {
super(instance)
dataBroker = broker;
_path = path;
}
-package class DataCommitHandlerRegistration<P extends Path<P>,D>
-extends AbstractObjectRegistration<DataCommitHandler<P, D>> {
+package class DataCommitHandlerRegistration<P extends Path<P>, D> extends AbstractObjectRegistration<DataCommitHandler<P, D>> {
- AbstractDataBroker<P,D,?> dataBroker;
+ AbstractDataBroker<P, D, ?> dataBroker;
@Property
val P path;
- new(P path, DataCommitHandler<P, D> instance,
- AbstractDataBroker<P,D,?> broker) {
+ new(P path, DataCommitHandler<P, D> instance, AbstractDataBroker<P, D, ?> broker) {
super(instance)
dataBroker = broker;
_path = path;
}
-package class TwoPhaseCommit<P extends Path<P>,D> implements Callable<RpcResult<TransactionStatus>> {
-
+package class TwoPhaseCommit<P extends Path<P>, D,DCL extends DataChangeListener<P, D>> implements Callable<RpcResult<TransactionStatus>> {
+
private static val log = LoggerFactory.getLogger(TwoPhaseCommit);
- val AbstractDataTransaction<P,D> transaction;
- val AbstractDataBroker<P,D,?> dataBroker;
+ val AbstractDataTransaction<P, D> transaction;
+ val AbstractDataBroker<P, D, DCL> dataBroker;
- new(AbstractDataTransaction<P,D> transaction, AbstractDataBroker<P,D,?> broker) {
+ new(AbstractDataTransaction<P, D> transaction, AbstractDataBroker<P, D, DCL> broker) {
this.transaction = transaction;
this.dataBroker = broker;
}
override call() throws Exception {
- val Iterable<DataCommitHandler<P, D>> commitHandlers = dataBroker.activeCommitHandlers;
+ // get affected paths
+ val affectedPaths = new HashSet<P>();
+
+ affectedPaths.addAll(transaction.createdConfigurationData.keySet);
+ affectedPaths.addAll(transaction.updatedConfigurationData.keySet);
+ affectedPaths.addAll(transaction.removedConfigurationData);
+
+ affectedPaths.addAll(transaction.createdOperationalData.keySet);
+ affectedPaths.addAll(transaction.updatedOperationalData.keySet);
+ affectedPaths.addAll(transaction.removedOperationalData);
+
+ val listeners = dataBroker.affectedListenersWithInitialState(affectedPaths);
// requesting commits
+ val Iterable<DataCommitHandler<P, D>> commitHandlers = dataBroker.affectedCommitHandlers(affectedPaths);
val List<DataCommitTransaction<P, D>> handlerTransactions = new ArrayList();
try {
for (handler : commitHandlers) {
handlerTransactions.add(handler.requestCommit(transaction));
}
} catch (Exception e) {
- log.error("Request Commit failded",e);
- return rollback(handlerTransactions,e);
+ log.error("Request Commit failded", e);
+ return rollback(handlerTransactions, e);
}
val List<RpcResult<Void>> results = new ArrayList();
try {
for (subtransaction : handlerTransactions) {
results.add(subtransaction.finish());
}
+ listeners.publishDataChangeEvent();
} catch (Exception e) {
- log.error("Finish Commit failed",e);
- return rollback(handlerTransactions,e);
+ log.error("Finish Commit failed", e);
+ return rollback(handlerTransactions, e);
}
+
return Rpcs.getRpcResult(true, TransactionStatus.COMMITED, Collections.emptySet());
+
+ }
+
+ def void publishDataChangeEvent(ImmutableList<ListenerStateCapture<P, D,DCL>> listeners) {
+ for(listenerSet : listeners) {
+ val updatedConfiguration = dataBroker.readConfigurationData(listenerSet.path);
+ val updatedOperational = dataBroker.readOperationalData(listenerSet.path);
+
+ val changeEvent = new DataChangeEventImpl(transaction,listenerSet.initialConfigurationState,listenerSet.initialOperationalState,updatedOperational,updatedConfiguration);
+ for(listener : listenerSet.listeners) {
+ try {
+ listener.instance.onDataChanged(changeEvent);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
- def rollback(List<DataCommitTransaction<P, D>> transactions,Exception e) {
+ def rollback(List<DataCommitTransaction<P, D>> transactions, Exception e) {
for (transaction : transactions) {
transaction.rollback()
}
+
// FIXME return encountered error.
return Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.emptySet());
}
}
-
public abstract class AbstractDataTransaction<P extends Path<P>, D> extends AbstractDataModification<P, D> {
@Property
private val Object identifier;
-
var TransactionStatus status;
-
-
+
var AbstractDataBroker<P, D, ?> broker;
- protected new (AbstractDataBroker<P,D,?> dataBroker) {
+ protected new(AbstractDataBroker<P, D, ?> dataBroker) {
super(dataBroker);
_identifier = new Object();
broker = dataBroker;
status = TransactionStatus.NEW;
- //listeners = new ListenerRegistry<>();
+
+ //listeners = new ListenerRegistry<>();
}
- override commit() {
+ override commit() {
return broker.commit(this);
}
return false;
if (getClass() != obj.getClass())
return false;
- val other = (obj as AbstractDataTransaction<P,D>) ;
+ val other = (obj as AbstractDataTransaction<P,D>);
if (broker == null) {
if (other.broker != null)
return false;
return status;
}
-
protected abstract def void onStatusChange(TransactionStatus status);
-
+
public def changeStatus(TransactionStatus status) {
this.status = status;
onStatusChange(status);
}
-
+
}
--- /dev/null
+package org.opendaylight.controller.md.sal.common.impl.service;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChange;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+
+public class DataChangeEventImpl<P, D> implements DataChangeEvent<P, D> {
+
+ private final DataChange<P, D> dataChange;
+
+ private final D originalConfigurationSubtree;
+ private final D originalOperationalSubtree;
+ private final D updatedOperationalSubtree;
+ private final D updatedConfigurationSubtree;
+
+
+
+
+ public DataChangeEventImpl(DataChange<P, D> dataChange, D originalConfigurationSubtree,
+ D originalOperationalSubtree, D updatedOperationalSubtree, D updatedConfigurationSubtree) {
+ super();
+ this.dataChange = dataChange;
+ this.originalConfigurationSubtree = originalConfigurationSubtree;
+ this.originalOperationalSubtree = originalOperationalSubtree;
+ this.updatedOperationalSubtree = updatedOperationalSubtree;
+ this.updatedConfigurationSubtree = updatedConfigurationSubtree;
+ }
+
+ @Override
+ public D getUpdatedOperationalSubtree() {
+ return updatedOperationalSubtree;
+ }
+
+ @Override
+ public D getUpdatedConfigurationSubtree() {
+ return updatedConfigurationSubtree;
+ }
+
+ public Map<P, D> getCreatedOperationalData() {
+ return dataChange.getCreatedOperationalData();
+ }
+
+ public Map<P, D> getCreatedConfigurationData() {
+ return dataChange.getCreatedConfigurationData();
+ }
+
+ public Map<P, D> getUpdatedOperationalData() {
+ return dataChange.getUpdatedOperationalData();
+ }
+
+ public Map<P, D> getUpdatedConfigurationData() {
+ return dataChange.getUpdatedConfigurationData();
+ }
+
+ public Set<P> getRemovedConfigurationData() {
+ return dataChange.getRemovedConfigurationData();
+ }
+
+ public Set<P> getRemovedOperationalData() {
+ return dataChange.getRemovedOperationalData();
+ }
+
+ public Map<P, D> getOriginalConfigurationData() {
+ return dataChange.getOriginalConfigurationData();
+ }
+
+ public Map<P, D> getOriginalOperationalData() {
+ return dataChange.getOriginalOperationalData();
+ }
+
+}
+++ /dev/null
-package org.opendaylight.controller.sal.connector.netconf.test;
-
-import static junit.framework.Assert.assertEquals;
-import static org.junit.Assert.*;
-import io.netty.channel.ChannelFuture;
-import io.netty.util.HashedWheelTimer;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import javax.net.ssl.SSLContext;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
-import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
-import org.opendaylight.controller.config.spi.ModuleFactory;
-import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService;
-import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory;
-import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
-import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
-import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.client.NetconfClient;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
-import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
-import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
-import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFactory;
-import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
-import org.opendaylight.controller.netconf.impl.SessionIdProvider;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
-import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.sal.connect.netconf.InventoryUtils;
-import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
-import org.opendaylight.controller.sal.connect.netconf.NetconfDeviceManager;
-import org.opendaylight.controller.sal.connect.netconf.NetconfInventoryUtils;
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.core.api.data.DataProviderService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.dom.broker.DataBrokerImpl;
-import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-public class MountTest extends AbstractConfigTest {
-
- private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
- private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024);
- private static final URI NETCONF_MONITORING_NS = URI.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring");
-
- private static final QName NETCONF_MONITORING = new QName(NETCONF_MONITORING_NS, new Date(2010,10,04), "ietf-netconf-monitoring");
- private static final QName NETCONF_MONITORING_STATE = new QName(NETCONF_MONITORING,"netconf-state");
-
-
- private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession;
- private DefaultCommitNotificationProducer commitNot;
- private NetconfServerDispatcher dispatch;
- private DataProviderService dataBroker;
- private MountPointManagerImpl mountManager;
- private NetconfDeviceManager netconfManager;
-
- private static QName CONFIG_MODULES = new QName(
- URI.create("urn:opendaylight:params:xml:ns:yang:controller:config"), null, "modules");
- private static QName CONFIG_SERVICES = new QName(
- URI.create("urn:opendaylight:params:xml:ns:yang:controller:config"), null, "modules");
-
- private NetconfClient createSession(final InetSocketAddress address, NetconfClientDispatcher dispatcher) throws InterruptedException {
- final NetconfClient netconfClient = new NetconfClient("test " + address.toString(), address, 5000, dispatcher);
- return netconfClient;
- }
-
- @Before
- public void setUp() throws Exception {
- super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
- new ModuleFactory[0])));
-
- loadMessages();
-
- NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
- factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore()));
-
- commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
-
- dispatch = createDispatcher(Optional.<SSLContext> absent(), factoriesListener);
- ChannelFuture s = dispatch.createServer(tcpAddress);
- s.await();
-
- dataBroker = new DataBrokerImpl();
- mountManager = new MountPointManagerImpl();
- mountManager.setDataBroker(dataBroker);
- netconfManager = new NetconfDeviceManager();
-
- netconfManager.setMountService(mountManager);
- netconfManager.setDataService(dataBroker);
- netconfManager.start();
-
- try (NetconfClient netconfClient = createSession(tcpAddress, netconfManager.getDispatcher())) {
- // send edit_config.xml
- final Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument();
- assertNotNull(rpcReply);
- }
- }
-
-
- protected List<ModuleFactory> getModuleFactories() {
- return getModuleFactoriesS();
- }
-
- static List<ModuleFactory> getModuleFactoriesS() {
- return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(),
- new NetconfTestImplModuleFactory());
- }
-
- private void loadMessages() throws IOException, SAXException, ParserConfigurationException {
- this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml");
- this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
- this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml");
- this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml");
- }
-
- private NetconfServerDispatcher createDispatcher(Optional<SSLContext> sslC,
- NetconfOperationServiceFactoryListenerImpl factoriesListener) {
- SessionIdProvider idProvider = new SessionIdProvider();
- NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
- new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
-
- NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
-
- return new NetconfServerDispatcher(sslC, serverNegotiatorFactory, listenerFactory);
- }
-
- private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException {
- final Collection<InputStream> yangDependencies = getBasicYangs();
- return new HardcodedYangStoreService(yangDependencies);
- }
-
- private Collection<InputStream> getBasicYangs() throws IOException {
- List<String> paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang",
- "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang",
- "/META-INF/yang/ietf-inet-types.yang");
- final Collection<InputStream> yangDependencies = new ArrayList<>();
- for (String path : paths) {
- final InputStream is = Preconditions
- .checkNotNull(getClass().getResourceAsStream(path), path + " not found");
- yangDependencies.add(is);
- }
- return yangDependencies;
- }
-
- @Test
- public void test() {
- // MountProvisionInstance mount =
- // Mockito.mock(MountProvisionInstance.class);
- InstanceIdentifier path = InstanceIdentifier.builder(InventoryUtils.INVENTORY_PATH)
- .node(InventoryUtils.INVENTORY_NODE).toInstance();
- netconfManager.netconfNodeAdded(path, tcpAddress);
- InstanceIdentifier mountPointPath = path;
- MountProvisionInstance mountPoint = mountManager.getMountPoint(mountPointPath);
-
- CompositeNode data = mountPoint.readOperationalData(InstanceIdentifier.builder().node(CONFIG_MODULES)
- .toInstance());
- assertNotNull(data);
- assertEquals(CONFIG_MODULES, data.getNodeType());
-
- CompositeNode data2 = mountPoint.readOperationalData(InstanceIdentifier.builder().toInstance());
- assertNotNull(data2);
-
- InstanceIdentifier fullPath = InstanceIdentifier.builder(mountPointPath).node(CONFIG_MODULES).toInstance();
-
- CompositeNode data3 = dataBroker.readOperationalData(fullPath);
- assertNotNull(data3);
- assertEquals(CONFIG_MODULES, data.getNodeType());
- }
-
-}
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.restconf.impl.StructuredData;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
/**
@GET
public Object getRoot();
-
@GET
@Path("/modules")
@Produces({API+JSON,API+XML})
@Produces({Draft02.MediaTypes.API+JSON,Draft02.MediaTypes.API+XML,API+JSON,API+XML})
public StructuredData invokeRpc(@PathParam("identifier") String identifier, CompositeNode payload);
-
@GET
@Path("/config/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML})
public StructuredData readConfigurationData(@PathParam("identifier") String identifier);
-
-
@PUT
@Path("/config/{identifier:.+}")
@Produces({API+JSON,API+XML})
- public RpcResult<TransactionStatus> createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
+ public Response createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
@POST
@Path("/config/{identifier:.+}")
@Produces({API+JSON,API+XML})
- public RpcResult<TransactionStatus> updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
+ public Response updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
@GET
@Path("/operational/{identifier:.+}")
@PUT
@Path("/operational/{identifier:.+}")
@Produces({API+JSON,API+XML})
- public RpcResult<TransactionStatus> createOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
+ public Response createOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
@POST
@Path("/operational/{identifier:.+}")
@Produces({API+JSON,API+XML})
- public RpcResult<TransactionStatus> updateOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
+ public Response updateOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
-
}
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.restconf.impl.StructuredData;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
public interface RestconfServiceLegacy {
@PUT
@Path("/datastore/{identifier:.+}")
@Produces({API+JSON,API+XML})
- public RpcResult<TransactionStatus> createConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
+ public Response createConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
@Deprecated
@POST
@Path("/datastore/{identifier:.+}")
@Produces({API+JSON,API+XML})
- public RpcResult<TransactionStatus> updateConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
+ public Response updateConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
}
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.net.URI;
import java.util.Map.Entry;
import java.util.Set;
class JsonReader {
- public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedJsonFormatException {
+ public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedFormatException {
JsonParser parser = new JsonParser();
-
+
JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
if (!rootElement.isJsonObject()) {
- throw new UnsupportedJsonFormatException("Root element of Json has to be Object");
+ throw new UnsupportedFormatException("Root element of Json has to be Object");
}
-
+
Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
if (entrySetsOfRootJsonObject.size() != 1) {
- throw new UnsupportedJsonFormatException("Json Object should contain one element");
+ throw new UnsupportedFormatException("Json Object should contain one element");
} else {
Entry<String, JsonElement> childEntry = Lists.newArrayList(entrySetsOfRootJsonObject).get(0);
String firstElementName = childEntry.getKey();
if (firstElementInArray.isJsonObject()) {
return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
}
- throw new UnsupportedJsonFormatException("Array as the first element in Json Object can have only Object element");
+ throw new UnsupportedFormatException(
+ "Array as the first element in Json Object can have only Object element");
}
}
- throw new UnsupportedJsonFormatException("First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
+ throw new UnsupportedFormatException(
+ "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
}
}
-
+
private CompositeNodeWrapper createStructureWithRoot(String rootObjectName, JsonObject rootObject) {
- CompositeNodeWrapper firstNode = new CompositeNodeWrapper(rootObjectName);
+ CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFrom(rootObjectName),
+ getLocalNameFrom(rootObjectName));
for (Entry<String, JsonElement> childOfFirstNode : rootObject.entrySet()) {
addChildToParent(childOfFirstNode.getKey(), childOfFirstNode.getValue(), firstNode);
}
return firstNode;
}
-
+
private void addChildToParent(String childName, JsonElement childType, CompositeNodeWrapper parent) {
if (childType.isJsonObject()) {
- CompositeNodeWrapper child = new CompositeNodeWrapper(childName);
+ CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFrom(childName),
+ getLocalNameFrom(childName));
parent.addValue(child);
for (Entry<String, JsonElement> childOfChild : childType.getAsJsonObject().entrySet()) {
addChildToParent(childOfChild.getKey(), childOfChild.getValue(), child);
}
} else if (childType.isJsonArray()) {
- for (JsonElement childOfChildType : childType.getAsJsonArray()) {
- addChildToParent(childName, childOfChildType, parent);
+ if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) {
+ parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), null));
+
+ } else {
+ for (JsonElement childOfChildType : childType.getAsJsonArray()) {
+ addChildToParent(childName, childOfChildType, parent);
+ }
}
} else if (childType.isJsonPrimitive()) {
JsonPrimitive childPrimitive = childType.getAsJsonPrimitive();
String value = childPrimitive.getAsString();
- SimpleNodeWrapper child = null;
- if (value.equals("[null]")) {
- child = new SimpleNodeWrapper(childName, null);
- } else {
- child = new SimpleNodeWrapper(childName, value);
- }
- parent.addValue(child);
+ parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), value));
+ }
+ }
+
+ private URI getNamespaceFrom(String jsonElementName) {
+ int indexOfDelimeter = jsonElementName.lastIndexOf(':');
+ if (indexOfDelimeter == -1) {
+ return null;
}
+ return URI.create(jsonElementName.substring(0, indexOfDelimeter));
}
+
+ private String getLocalNameFrom(String jsonElementName) {
+ int indexOfDelimeter = jsonElementName.lastIndexOf(':');
+ if (indexOfDelimeter == -1) {
+ return jsonElementName;
+ }
+ return jsonElementName.substring(indexOfDelimeter + 1, jsonElementName.length());
+ }
+
}
JsonReader jsonReader = new JsonReader();
try {
return jsonReader.read(entityStream);
- } catch (UnsupportedJsonFormatException e) {
- throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
+ } catch (UnsupportedFormatException e) {
+ throw new WebApplicationException(e,Response.status(Response.Status.BAD_REQUEST)
.entity(e.getMessage()).build());
}
}
singletons.add(restconfImpl);
singletons.add(XmlToCompositeNodeProvider.INSTANCE);
singletons.add(StructuredDataToXmlProvider.INSTANCE);
+ singletons.add(JsonToCompositeNodeProvider.INSTANCE);
+ singletons.add(StructuredDataToJsonProvider.INSTANCE);
return singletons;
}
package org.opendaylight.controller.sal.rest.impl;
-import org.opendaylight.controller.sal.core.api.AbstractProvider;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.core.api.data.DataProviderService;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
-public class RestconfProvider extends AbstractProvider {
+public class RestconfProvider implements BundleActivator, Provider, ServiceTrackerCustomizer<Broker, Broker> {
+ public final static String NOT_INITALIZED_MSG = "Restcof is not initialized yet. Please try again later";
+
private ListenerRegistration<SchemaServiceListener> listenerRegistration;
+ private ServiceTracker<Broker, Broker> brokerServiceTrancker;
+ private BundleContext bundleContext;
+ private ProviderSession session;
@Override
public void onSessionInitiated(ProviderSession session) {
}
@Override
- protected void stopImpl(BundleContext context) {
- super.stopImpl(context);
+ public void start(BundleContext context) throws Exception {
+ bundleContext = context;
+ brokerServiceTrancker = new ServiceTracker<>(context, Broker.class, this);
+ brokerServiceTrancker.open();
+ }
+
+ @Override
+ public void stop(BundleContext context) {
if (listenerRegistration != null) {
try {
listenerRegistration.close();
e.printStackTrace();
}
}
+ session.close();
+ brokerServiceTrancker.close();
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Broker addingService(ServiceReference<Broker> reference) {
+ Broker broker = bundleContext.getService(reference);
+ broker.registerProvider(this, bundleContext);
+ return broker;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<Broker> reference, Broker service) {
+ // NOOP
+ }
+
+ @Override
+ public void removedService(ServiceReference<Broker> reference, Broker service) {
+ bundleContext.ungetService(reference);
+ BrokerFacade.getInstance().setContext(null);
+ BrokerFacade.getInstance().setDataService(null);
+ ControllerContext.getInstance().setSchemas(null);
}
}
import com.google.gson.stream.JsonWriter;
@Provider
-@Produces({ API + RestconfService.JSON })
+@Produces({API+RestconfService.JSON})
public enum StructuredDataToJsonProvider implements MessageBodyWriter<StructuredData> {
INSTANCE;
--- /dev/null
+package org.opendaylight.controller.sal.rest.impl;
+
+public class UnsupportedFormatException extends Exception {
+
+ private static final long serialVersionUID = -1741388894406313402L;
+
+ public UnsupportedFormatException() {
+ super();
+ }
+
+ public UnsupportedFormatException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public UnsupportedFormatException(String message) {
+ super(message);
+ }
+
+ public UnsupportedFormatException(Throwable cause) {
+ super(cause);
+ }
+
+}
+++ /dev/null
-package org.opendaylight.controller.sal.rest.impl;
-
-public class UnsupportedJsonFormatException extends Exception {
-
- private static final long serialVersionUID = -1741388894406313402L;
-
- public UnsupportedJsonFormatException() {
- super();
- }
-
- public UnsupportedJsonFormatException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public UnsupportedJsonFormatException(String message) {
- super(message);
- }
-
- public UnsupportedJsonFormatException(Throwable cause) {
- super(cause);
- }
-
-}
--- /dev/null
+package org.opendaylight.controller.sal.rest.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Stack;
+
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+
+public class XmlReader {
+
+ private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
+ private XMLEventReader eventReader;
+
+ public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
+ eventReader = xmlInputFactory.createXMLEventReader(entityStream);
+
+ if (eventReader.hasNext()) {
+ XMLEvent element = eventReader.peek();
+ if (element.isStartDocument()) {
+ eventReader.nextEvent();
+ }
+ }
+
+ if (eventReader.hasNext() && !isCompositeNodeEvent(eventReader.peek())) {
+ throw new UnsupportedFormatException("Root element of XML has to be composite element.");
+ }
+
+ final Stack<NodeWrapper<?>> processingQueue = new Stack<>();
+ CompositeNodeWrapper root = null;
+ NodeWrapper<?> element = null;
+ while (eventReader.hasNext()) {
+ final XMLEvent event = eventReader.nextEvent();
+
+ if (event.isStartElement()) {
+ final StartElement startElement = event.asStartElement();
+ CompositeNodeWrapper compParentNode = null;
+ if (!processingQueue.isEmpty() && processingQueue.peek() instanceof CompositeNodeWrapper) {
+ compParentNode = (CompositeNodeWrapper) processingQueue.peek();
+ }
+ NodeWrapper<?> newNode = null;
+ if (isCompositeNodeEvent(event)) {
+ if (root == null) {
+ root = resolveCompositeNodeFromStartElement(startElement);
+ newNode = root;
+ } else {
+ newNode = resolveCompositeNodeFromStartElement(startElement);
+ }
+ } else if (isSimpleNodeEvent(event)) {
+ if (root == null) {
+ throw new UnsupportedFormatException("Root element of XML has to be composite element.");
+ }
+ newNode = resolveSimpleNodeFromStartElement(startElement);
+ }
+
+ if (newNode != null) {
+ processingQueue.push(newNode);
+ if (compParentNode != null) {
+ compParentNode.addValue(newNode);
+ }
+ }
+ } else if (event.isEndElement()) {
+ element = processingQueue.pop();
+ }
+ }
+
+ if (!root.getLocalName().equals(element.getLocalName())) {
+ throw new UnsupportedFormatException("XML should contain only one root element");
+ }
+
+ return root;
+ }
+
+ private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
+ checkArgument(event != null, "XML Event cannot be NULL!");
+ if (event.isStartElement()) {
+ if (eventReader.hasNext()) {
+ final XMLEvent innerEvent;
+ innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ final Characters chars = innerEvent.asCharacters();
+ if (!chars.isWhiteSpace()) {
+ return true;
+ }
+ } else if (innerEvent.isEndElement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
+ checkArgument(event != null, "XML Event cannot be NULL!");
+ if (event.isStartElement()) {
+ if (eventReader.hasNext()) {
+ XMLEvent innerEvent;
+ innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ Characters chars = innerEvent.asCharacters();
+ if (chars.isWhiteSpace()) {
+ eventReader.nextEvent();
+ innerEvent = eventReader.peek();
+ }
+ }
+ if (innerEvent.isStartElement()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private SimpleNodeWrapper resolveSimpleNodeFromStartElement(final StartElement startElement) throws XMLStreamException {
+ checkArgument(startElement != null, "Start Element cannot be NULL!");
+ String data = null;
+
+ if (eventReader.hasNext()) {
+ final XMLEvent innerEvent = eventReader.peek();
+ if (innerEvent.isCharacters()) {
+ final Characters chars = innerEvent.asCharacters();
+ if (!chars.isWhiteSpace()) {
+ data = innerEvent.asCharacters().getData();
+ }
+ } else if (innerEvent.isEndElement()) {
+ data = "";
+ }
+ }
+
+ return new SimpleNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement), data);
+ }
+
+ private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
+ checkArgument(startElement != null, "Start Element cannot be NULL!");
+ return new CompositeNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement));
+ }
+
+ private String getLocalNameFrom(StartElement startElement) {
+ return startElement.getName().getLocalPart();
+ }
+
+ private URI getNamespaceFrom(StartElement startElement) {
+ String namespaceURI = startElement.getName().getNamespaceURI();
+ return namespaceURI.isEmpty() ? null : URI.create(namespaceURI);
+ }
+
+}
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
@Provider
-@Consumes({API+RestconfService.XML})
+@Consumes({ API + RestconfService.XML })
public enum XmlToCompositeNodeProvider implements MessageBodyReader<CompositeNode> {
INSTANCE;
-
- private final static Logger logger = LoggerFactory.getLogger(XmlToCompositeNodeProvider.class);
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
public CompositeNode readFrom(Class<CompositeNode> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
+ XmlReader xmlReader = new XmlReader();
try {
- Node<?> node = XmlTreeBuilder.buildDataTree(entityStream);
- if (node instanceof SimpleNode) {
- logger.info("Node is SimpleNode");
- throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
- .entity("XML should start with XML element that contains 1..N XML child elements.").build());
- }
- return (CompositeNode) node;
- } catch (XMLStreamException e) {
- logger.info("Error during translation of InputStream to Node\n" + e.getMessage());
- throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST)
- .entity(e.getMessage()).build());
+ return xmlReader.read(entityStream);
+ } catch (XMLStreamException | UnsupportedFormatException e) {
+ throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage())
+ .build());
}
}
package org.opendaylight.controller.sal.restconf.impl
+import javax.ws.rs.WebApplicationException
+import javax.ws.rs.core.Response
import org.opendaylight.controller.md.sal.common.api.data.DataReader
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession
import org.opendaylight.controller.sal.core.api.data.DataBrokerService
+import org.opendaylight.controller.sal.rest.impl.RestconfProvider
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.common.RpcResult
import org.opendaylight.yangtools.yang.data.api.CompositeNode
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import static org.opendaylight.controller.sal.restconf.impl.BrokerFacade.*
class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
throw new IllegalStateException("Already instantiated");
}
}
-
+
def static BrokerFacade getInstance() {
return INSTANCE
}
+ private def void checkPreconditions() {
+ if (context == null || dataService == null) {
+ throw new WebApplicationException(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity(RestconfProvider::NOT_INITALIZED_MSG).build())
+ }
+ }
+
override readConfigurationData(InstanceIdentifier path) {
+ checkPreconditions
return dataService.readConfigurationData(path);
}
override readOperationalData(InstanceIdentifier path) {
+ checkPreconditions
return dataService.readOperationalData(path);
}
def RpcResult<CompositeNode> invokeRpc(QName type, CompositeNode payload) {
+ checkPreconditions
val future = context.rpc(type, payload);
return future.get;
}
def commitConfigurationDataPut(InstanceIdentifier path, CompositeNode payload) {
+ checkPreconditions
val transaction = dataService.beginTransaction;
transaction.putConfigurationData(path, payload);
return transaction.commit()
}
def commitOperationalDataPut(InstanceIdentifier path, CompositeNode payload) {
+ checkPreconditions
val transaction = dataService.beginTransaction;
transaction.putOperationalData(path, payload);
return transaction.commit()
public CompositeNodeWrapper(String localName) {
this.localName = Preconditions.checkNotNull(localName);
}
+
+ public CompositeNodeWrapper(URI namespace, String localName) {
+ this(localName);
+ this.namespace = namespace;
+ }
@Override
public String getLocalName() {
import java.util.HashMap
import java.util.List
import java.util.Map
+import java.util.concurrent.ConcurrentHashMap
+import javax.ws.rs.WebApplicationException
+import javax.ws.rs.core.Response
+import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
+import org.opendaylight.controller.sal.rest.impl.RestconfProvider
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import static com.google.common.base.Preconditions.*
-import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition
-import java.util.concurrent.ConcurrentHashMap
class ControllerContext implements SchemaServiceListener {
static def getInstance() {
return INSTANCE
}
+
+ private def void checkPreconditions() {
+ if (schemas == null) {
+ throw new WebApplicationException(Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity(RestconfProvider::NOT_INITALIZED_MSG).build())
+ }
+ }
public def InstanceIdWithSchemaNode toInstanceIdentifier(String restconfInstance) {
val ret = InstanceIdentifier.builder();
}
private def findModule(String restconfInstance) {
+ checkPreconditions
checkNotNull(restconfInstance);
val pathArgs = restconfInstance.split("/");
if (pathArgs.empty) {
}
def String toFullRestconfIdentifier(InstanceIdentifier path) {
+ checkPreconditions
val elements = path.path;
val ret = new StringBuilder();
val startQName = elements.get(0).nodeType;
}
def CharSequence toRestconfIdentifier(QName qname) {
+ checkPreconditions
var module = uriToModuleName.get(qname.namespace)
if (module == null) {
val moduleSchema = schemas.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
package org.opendaylight.controller.sal.restconf.impl
import java.util.List
+import javax.ws.rs.core.Response
import org.opendaylight.controller.sal.rest.api.RestconfService
import org.opendaylight.yangtools.yang.data.api.CompositeNode
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus
class RestconfImpl implements RestconfService {
override createConfigurationData(String identifier, CompositeNode payload) {
val identifierWithSchemaNode = identifier.toInstanceIdentifier
val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
- return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
+ default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+ }
}
override updateConfigurationData(String identifier, CompositeNode payload) {
val identifierWithSchemaNode = identifier.toInstanceIdentifier
val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
- return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
+ default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+ }
}
override invokeRpc(String identifier, CompositeNode payload) {
override createOperationalData(String identifier, CompositeNode payload) {
val identifierWithSchemaNode = identifier.toInstanceIdentifier
val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
- return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ val status = broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
+ default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+ }
}
override updateOperationalData(String identifier, CompositeNode payload) {
val identifierWithSchemaNode = identifier.toInstanceIdentifier
val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
- return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ val status = broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
+ default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+ }
}
private def CompositeNode resolveNodeNamespaceBySchema(CompositeNode node, DataSchemaNode schema) {
val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
for (child : children) {
addNamespaceToNodeFromSchemaRecursively(child,
- (schema as DataNodeContainer).childNodes.findFirst[n|n.QName.localName === child.localName])
+ (schema as DataNodeContainer).childNodes.findFirst[n|n.QName.localName.equals(child.localName)])
}
}
}
this.value = value;
}
+ public SimpleNodeWrapper(URI namespace, String localName, String value) {
+ this(localName, value);
+ this.namespace = namespace;
+ }
+
@Override
public String getLocalName() {
if (simpleNode != null) {
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import java.util.concurrent.*;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public class DummyFuture implements Future<RpcResult<TransactionStatus>> {
+
+ private final boolean cancel;
+ private final boolean isCancelled;
+ private final boolean isDone;
+ private final RpcResult<TransactionStatus> result;
+
+ public DummyFuture() {
+ cancel = false;
+ isCancelled = false;
+ isDone = false;
+ result = null;
+ }
+
+ private DummyFuture(Builder builder) {
+ cancel = builder.cancel;
+ isCancelled = builder.isCancelled;
+ isDone = builder.isDone;
+ result = builder.result;
+ }
+
+ public static Builder builder() {
+ return new DummyFuture.Builder();
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return cancel;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+
+ @Override
+ public boolean isDone() {
+ return isDone;
+ }
+
+ @Override
+ public RpcResult<TransactionStatus> get() throws InterruptedException, ExecutionException {
+ return result;
+ }
+
+ @Override
+ public RpcResult<TransactionStatus> get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return result;
+ }
+
+ public static class Builder {
+
+ private boolean cancel;
+ private boolean isCancelled;
+ private boolean isDone;
+ private RpcResult<TransactionStatus> result;
+
+ public Builder cancel(boolean cancel) {
+ this.cancel = cancel;
+ return this;
+ }
+
+ public Builder isCancelled(boolean isCancelled) {
+ this.isCancelled = isCancelled;
+ return this;
+ }
+
+ public Builder isDone(boolean isDone) {
+ this.isDone = isDone;
+ return this;
+ }
+
+ public Builder rpcResult(RpcResult<TransactionStatus> result) {
+ this.result = result;
+ return this;
+ }
+
+ public Future<RpcResult<TransactionStatus>> build() {
+ return new DummyFuture(this);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public class DummyRpcResult implements RpcResult<TransactionStatus> {
+
+ private final boolean isSuccessful;
+ private final TransactionStatus result;
+ private final Collection<RpcError> errors;
+
+ public DummyRpcResult() {
+ isSuccessful = false;
+ result = null;
+ errors = null;
+ }
+
+ private DummyRpcResult(Builder builder) {
+ isSuccessful = builder.isSuccessful;
+ result = builder.result;
+ errors = builder.errors;
+ }
+
+ public static Builder builder() {
+ return new DummyRpcResult.Builder();
+ }
+
+ @Override
+ public boolean isSuccessful() {
+ return isSuccessful;
+ }
+
+ @Override
+ public TransactionStatus getResult() {
+ return result;
+ }
+
+ @Override
+ public Collection<RpcError> getErrors() {
+ return errors;
+ }
+
+ public static class Builder {
+ private boolean isSuccessful;
+ private TransactionStatus result;
+ private Collection<RpcError> errors;
+
+ public Builder isSuccessful(boolean isSuccessful) {
+ this.isSuccessful = isSuccessful;
+ return this;
+ }
+
+ public Builder result(TransactionStatus result) {
+ this.result = result;
+ return this;
+ }
+
+ public Builder errors(Collection<RpcError> errors) {
+ this.errors = errors;
+ return this;
+ }
+
+ public RpcResult<TransactionStatus> build() {
+ return new DummyRpcResult(this);
+ }
+
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import javax.ws.rs.WebApplicationException;
+
+import org.junit.*;
+import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.*;
+import org.opendaylight.yangtools.yang.data.api.*;
+import org.slf4j.*;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.model.api.*;
+
+import com.google.gson.JsonSyntaxException;
+
+public class FromJsonToCompositeNodeTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FromJsonToCompositeNodeTest.class);
+
+ @Test
+ public void simpleListTest() {
+ simpleTest("/json-to-composite-node/simple-list.json", "/json-to-composite-node/simple-list-yang", "lst",
+ "simple:list:yang1", "simple-list-yang1");
+ }
+
+ @Test
+ public void simpleContainerTest() {
+ simpleTest("/json-to-composite-node/simple-container.json", "/json-to-composite-node/simple-container-yang",
+ "cont", "simple:container:yang", "simple-container-yang");
+ }
+
+ /**
+ * test if for every leaf list item is simple node instance created
+ */
+ @Test
+ public void multipleItemsInLeafList() {
+ CompositeNode compositeNode = compositeContainerFromJson(
+ "/json-to-composite-node/multiple-leaflist-items.json", true);
+ assertNotNull(compositeNode);
+ assertEquals(3, compositeNode.getChildren().size());
+
+ boolean lflst1_1 = false;
+ boolean lflst1_2 = false;
+ boolean lflst1_3 = false;
+
+ for (Node<?> node : compositeNode.getChildren()) {
+ assertEquals("lflst1", node.getNodeType().getLocalName());
+ assertTrue(node instanceof SimpleNode<?>);
+ SimpleNode<?> simpleNode = (SimpleNode<?>) node;
+ if (simpleNode.getValue().equals("45")) {
+ lflst1_1 = true;
+ } else if (simpleNode.getValue().equals("55")) {
+ lflst1_2 = true;
+ } else if (simpleNode.getValue().equals("66")) {
+ lflst1_3 = true;
+ }
+ }
+
+ assertTrue(lflst1_1);
+ assertTrue(lflst1_2);
+ assertTrue(lflst1_3);
+
+ }
+
+ /**
+ * List contains 4 items and in every item are other elements. It is
+ * supposed that there should be: lf11, lflst11, cont11, lst11
+ */
+ @Test
+ public void multipleItemsInListTest() {
+ CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/multiple-items-in-list.json",
+ true);
+ assertNotNull(compositeNode);
+
+ assertEquals("lst", compositeNode.getNodeType().getLocalName());
+
+ verityMultipleItemsInList(compositeNode);
+ }
+
+ @Test
+ public void nullArrayToCompositeNodeWithNullValueTest() {
+ CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/array-with-null.json", true);
+ assertNotNull(compositeNode);
+ assertEquals("cont", compositeNode.getNodeType().getLocalName());
+
+ assertNotNull(compositeNode.getChildren());
+ assertEquals(1, compositeNode.getChildren().size());
+ Node<?> lfNode = compositeNode.getChildren().iterator().next();
+
+ assertTrue(lfNode instanceof SimpleNode<?>);
+ assertEquals(null, ((SimpleNode<?>) lfNode).getValue());
+
+ }
+
+ @Test
+ public void incorrectTopLevelElementsTest() {
+ Throwable cause1 = null;
+ try {
+ compositeContainerFromJson("/json-to-composite-node/wrong-top-level1.json", true);
+ } catch (WebApplicationException e) {
+ cause1 = e;
+ }
+
+ assertNotNull(cause1);
+ assertTrue(cause1
+ .getCause()
+ .getMessage()
+ .contains(
+ "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."));
+
+ Throwable cause2 = null;
+ try {
+ compositeContainerFromJson("/json-to-composite-node/wrong-top-level2.json", true);
+ } catch (WebApplicationException e) {
+ cause2 = e;
+ }
+ assertNotNull(cause2);
+ assertTrue(cause2.getCause().getMessage().contains("Json Object should contain one element"));
+
+ Throwable cause3 = null;
+ try {
+ compositeContainerFromJson("/json-to-composite-node/wrong-top-level3.json", true);
+ } catch (WebApplicationException e) {
+ cause3 = e;
+ }
+ assertNotNull(cause3);
+ assertTrue(cause3
+ .getCause()
+ .getMessage()
+ .contains(
+ "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."));
+
+ }
+
+ /**
+ * if leaf list with no data is in json then no corresponding data is
+ * created in composite node. if leaf with no data then exception is raised
+ */
+ @Test
+ public void emptyDataReadTest() {
+ CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/empty-data.json", true);
+
+ assertNotNull(compositeNode);
+
+ assertEquals("cont", compositeNode.getNodeType().getLocalName());
+ assertTrue(compositeNode instanceof CompositeNode);
+ List<Node<?>> children = ((CompositeNode) compositeNode).getChildren();
+ assertEquals(1, children.size());
+ assertEquals("lflst2", children.get(0).getNodeType().getLocalName());
+ assertEquals("45", children.get(0).getValue());
+
+ String reason = null;
+ try {
+ compositeContainerFromJson("/json-to-composite-node/empty-data1.json", true);
+ } catch (JsonSyntaxException e) {
+ reason = e.getMessage();
+ }
+
+ assertTrue(reason.contains("Expected value at line"));
+
+ }
+
+ /**
+ * Tests whether namespace <b>stay unchanged</b> if concrete values are
+ * present in composite or simple node and if the method for update is
+ * called.
+ *
+ */
+ @Test
+ public void notSupplyNamespaceIfAlreadySupplied() {
+
+ CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/simple-list.json");
+ assertNotNull(compositeNode);
+
+ DataSchemaNode dataSchemaNode1 = null;
+ DataSchemaNode dataSchemaNode2 = null;
+ try {
+ dataSchemaNode1 = TestUtils.obtainSchemaFromYang("/json-to-composite-node/simple-list-yang",
+ "simple-list-yang1");
+ dataSchemaNode2 = TestUtils.obtainSchemaFromYang("/json-to-composite-node/simple-list-yang",
+ "simple-list-yang2");
+ } catch (FileNotFoundException e) {
+ LOG.error(e.getMessage());
+ assertTrue(false);
+ }
+ assertNotNull(dataSchemaNode1);
+ assertNotNull(dataSchemaNode2);
+
+ // supplement namespaces according to first data schema -
+ // "simple:data:types1"
+ TestUtils.supplementNamespace(dataSchemaNode1, compositeNode);
+
+ assertTrue(compositeNode instanceof CompositeNodeWrapper);
+ CompositeNode compNode = ((CompositeNodeWrapper) compositeNode).unwrap(null);
+
+ assertEquals("lst", compNode.getNodeType().getLocalName());
+ verifyCompositeNode(compNode, "simple:list:yang1");
+
+ // dataSchemaNode2 should't be taken into account, because compNode
+ // isn't CompositeNodeWrapper
+ TestUtils.supplementNamespace(dataSchemaNode2, compNode);
+ verifyCompositeNode(compNode, "simple:list:yang1");
+
+ }
+
+ private void simpleTest(String jsonPath, String yangPath, String topLevelElementName, String namespace,
+ String moduleName) {
+ CompositeNode compositeNode = compositeContainerFromJson(jsonPath);
+ assertNotNull(compositeNode);
+
+ DataSchemaNode dataSchemaNode = null;
+ try {
+ dataSchemaNode = TestUtils.obtainSchemaFromYang(yangPath, moduleName);
+ } catch (FileNotFoundException e) {
+ LOG.error(e.getMessage());
+ assertTrue(false);
+ }
+ assertNotNull(dataSchemaNode);
+
+ TestUtils.supplementNamespace(dataSchemaNode, compositeNode);
+
+ assertTrue(compositeNode instanceof CompositeNodeWrapper);
+ CompositeNode compNode = ((CompositeNodeWrapper) compositeNode).unwrap(null);
+
+ assertEquals(topLevelElementName, compNode.getNodeType().getLocalName());
+ verifyCompositeNode(compNode, namespace);
+ }
+
+ private void verityMultipleItemsInList(CompositeNode compositeNode) {
+ List<Node<?>> childrenNodes = compositeNode.getChildren();
+ assertEquals(4, childrenNodes.size());
+ boolean lf11Found = false;
+ boolean cont11Found = false;
+ boolean lst11Found = false;
+ for (Node<?> lst1Item : childrenNodes) {
+ assertEquals("lst1", lst1Item.getNodeType().getLocalName());
+ assertTrue(lst1Item instanceof CompositeNode);
+
+ List<Node<?>> childrenLst1 = ((CompositeNode) lst1Item).getChildren();
+ assertEquals(1, childrenLst1.size());
+ String localName = childrenLst1.get(0).getNodeType().getLocalName();
+ if (localName.equals("lf11")) {
+ assertTrue(childrenLst1.get(0) instanceof SimpleNode);
+ lf11Found = true;
+ } else if (localName.equals("lflst11")) {
+ assertTrue(childrenLst1.get(0) instanceof SimpleNode);
+ assertEquals("45", ((SimpleNode<?>) childrenLst1.get(0)).getValue());
+ lf11Found = true;
+ } else if (localName.equals("cont11")) {
+ assertTrue(childrenLst1.get(0) instanceof CompositeNode);
+ cont11Found = true;
+ } else if (localName.equals("lst11")) {
+ lst11Found = true;
+ assertTrue(childrenLst1.get(0) instanceof CompositeNode);
+ assertEquals(0, ((CompositeNode) childrenLst1.get(0)).getChildren().size());
+ }
+
+ }
+ assertTrue(lf11Found);
+ assertTrue(cont11Found);
+ assertTrue(lst11Found);
+ }
+
+ private void verifyCompositeNode(CompositeNode compositeNode, String namespace) {
+ boolean cont1Found = false;
+ boolean lst1Found = false;
+ boolean lflst1_1Found = false;
+ boolean lflst1_2Found = false;
+ boolean lf1Found = false;
+
+ assertEquals(namespace, compositeNode.getNodeType().getNamespace().toString());
+
+ for (Node<?> node : compositeNode.getChildren()) {
+ if (node.getNodeType().getLocalName().equals("cont1")) {
+ if (node instanceof CompositeNode) {
+ cont1Found = true;
+ assertEquals(0, ((CompositeNode) node).getChildren().size());
+ }
+ } else if (node.getNodeType().getLocalName().equals("lst1")) {
+ if (node instanceof CompositeNode) {
+ lst1Found = true;
+ assertEquals(0, ((CompositeNode) node).getChildren().size());
+ }
+ } else if (node.getNodeType().getLocalName().equals("lflst1")) {
+ if (node instanceof SimpleNode) {
+ if (((SimpleNode<?>) node).getValue().equals("lflst1_1")) {
+ lflst1_1Found = true;
+ } else if (((SimpleNode<?>) node).getValue().equals("lflst1_2")) {
+ lflst1_2Found = true;
+ }
+ }
+
+ } else if (node.getNodeType().getLocalName().equals("lf1")) {
+ if (node instanceof SimpleNode) {
+ if (((SimpleNode<?>) node).getValue().equals("lf1")) {
+ lf1Found = true;
+ }
+ }
+ }
+ assertEquals(namespace, node.getNodeType().getNamespace().toString());
+ }
+ assertTrue(cont1Found);
+ assertTrue(lst1Found);
+ assertTrue(lflst1_1Found);
+ assertTrue(lflst1_2Found);
+ assertTrue(lf1Found);
+ }
+
+ private CompositeNode compositeContainerFromJson(String jsonPath) {
+ return compositeContainerFromJson(jsonPath, false);
+ }
+
+ private CompositeNode compositeContainerFromJson(String jsonPath, boolean dummyNamespaces)
+ throws WebApplicationException {
+
+ JsonToCompositeNodeProvider jsonToCompositeNodeProvider = JsonToCompositeNodeProvider.INSTANCE;
+ InputStream jsonStream = FromJsonToCompositeNodeTest.class.getResourceAsStream(jsonPath);
+ try {
+ CompositeNode compositeNode = jsonToCompositeNodeProvider
+ .readFrom(null, null, null, null, null, jsonStream);
+ assertTrue(compositeNode instanceof CompositeNodeWrapper);
+ if (dummyNamespaces) {
+ try {
+ TestUtils.addDummyNamespaceToAllNodes((CompositeNodeWrapper) compositeNode);
+ return ((CompositeNodeWrapper) compositeNode).unwrap(null);
+ } catch (URISyntaxException e) {
+ LOG.error(e.getMessage());
+ assertTrue(e.getMessage(), false);
+ }
+ }
+ return compositeNode;
+ } catch (IOException e) {
+ LOG.error(e.getMessage());
+ assertTrue(e.getMessage(), false);
+ }
+ return null;
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.*;
+import java.net.URISyntaxException;
+
+import javax.ws.rs.WebApplicationException;
+
+import org.junit.*;
+import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.yangtools.yang.data.api.*;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.slf4j.*;
+
+public class FromXmlToCompositeNodeTest {
+ private static final Logger LOG = LoggerFactory.getLogger(FromXmlToCompositeNodeTest.class);
+
+ /**
+ * top level element represents container. second level element is list with
+ * two elements.
+ */
+ @Test
+ public void testXmlDataContainer() {
+ CompositeNode compNode = compositeContainerFromXml("/xml-to-composite-node/data-container.xml", false);
+ assertNotNull(compNode);
+ DataSchemaNode dataSchemaNode = null;
+ try {
+ dataSchemaNode = TestUtils.obtainSchemaFromYang("/xml-to-composite-node/data-container-yang");
+ } catch (FileNotFoundException e) {
+ LOG.error(e.getMessage());
+ assertTrue(false);
+ }
+
+ assertNotNull(dataSchemaNode);
+ TestUtils.supplementNamespace(dataSchemaNode, compNode);
+
+ String nameSpace = "data:container:yang";
+ assertEquals(nameSpace, compNode.getNodeType().getNamespace().toString());
+
+ verifyCommonPartAOfXml(compNode, "", nameSpace);
+ }
+
+ @Test
+ public void testXmlDataList() {
+ CompositeNode compNode = compositeContainerFromXml("/xml-to-composite-node/data-list.xml", false);
+ assertNotNull(compNode);
+
+ DataSchemaNode dataSchemaNode = null;
+ try {
+ dataSchemaNode = TestUtils.obtainSchemaFromYang("/xml-to-composite-node/data-list-yang",
+ "data-container-yang");
+ } catch (FileNotFoundException e) {
+ LOG.error(e.getMessage());
+ }
+ assertNotNull(dataSchemaNode);
+ TestUtils.supplementNamespace(dataSchemaNode, compNode);
+
+ String nameSpaceList = "data:list:yang";
+ String nameSpaceCont = "data:container:yang";
+ assertEquals(nameSpaceCont, compNode.getNodeType().getNamespace().toString());
+ assertEquals("cont", compNode.getNodeType().getLocalName());
+ assertEquals(3, compNode.getChildren().size());
+ CompositeNode lst1_1 = null;
+ CompositeNode lst1_2 = null;
+ int loopCount = 0;
+ for (Node<?> node : compNode.getChildren()) {
+ if (node.getNodeType().getLocalName().equals("lf1")) {
+ assertEquals(nameSpaceList, node.getNodeType().getNamespace().toString());
+ assertTrue(node instanceof SimpleNode<?>);
+ assertEquals("lf1", node.getValue());
+ } else {
+ assertTrue(node instanceof CompositeNode);
+ switch (loopCount++) {
+ case 0:
+ lst1_1 = (CompositeNode) node;
+ break;
+ case 1:
+ lst1_2 = (CompositeNode) node;
+ break;
+ }
+ assertEquals(nameSpaceCont, node.getNodeType().getNamespace().toString());
+ }
+ }
+ // lst1_1
+ verifyCommonPartAOfXml(lst1_1, "1", nameSpaceCont);
+ // :lst1_1
+
+ // lst1_2
+ SimpleNode<?> lflst11 = null;
+ CompositeNode cont11 = null;
+ for (Node<?> node : lst1_2.getChildren()) {
+ String nodeName = node.getNodeType().getLocalName();
+ if (nodeName.equals("lflst11")) {
+ assertTrue(node instanceof SimpleNode<?>);
+ lflst11 = (SimpleNode<?>) node;
+
+ } else if (nodeName.equals("cont11")) {
+ assertTrue(node instanceof CompositeNode);
+ cont11 = (CompositeNode) node;
+ }
+ assertEquals(nameSpaceCont, compNode.getNodeType().getNamespace().toString());
+ }
+ assertEquals("221", lflst11.getValue());
+
+ assertEquals(1, cont11.getChildren().size());
+ assertTrue(cont11.getChildren().get(0) instanceof SimpleNode<?>);
+ SimpleNode<?> cont11_lf111 = (SimpleNode<?>) cont11.getChildren().get(0);
+ assertEquals(nameSpaceCont, cont11_lf111.getNodeType().getNamespace().toString());
+ assertEquals("lf111", cont11_lf111.getNodeType().getLocalName());
+ assertEquals("100", cont11_lf111.getValue());
+ // :lst1_2
+
+ }
+
+ @Test
+ public void testXmlEmptyData() {
+ CompositeNode compNode = compositeContainerFromXml("/xml-to-composite-node/empty-data.xml", true);
+ assertEquals("cont", compNode.getNodeType().getLocalName());
+ SimpleNode<?> lf1 = null;
+ SimpleNode<?> lflst1_1 = null;
+ SimpleNode<?> lflst1_2 = null;
+ CompositeNode lst1 = null;
+ int lflst1Count = 0;
+ for (Node<?> node : compNode.getChildren()) {
+ if (node.getNodeType().getLocalName().equals("lf1")) {
+ assertTrue(node instanceof SimpleNode<?>);
+ lf1 = (SimpleNode<?>) node;
+ } else if (node.getNodeType().getLocalName().equals("lflst1")) {
+ assertTrue(node instanceof SimpleNode<?>);
+
+ switch (lflst1Count++) {
+ case 0:
+ lflst1_1 = (SimpleNode<?>) node;
+ break;
+ case 1:
+ lflst1_2 = (SimpleNode<?>) node;
+ break;
+ }
+ } else if (node.getNodeType().getLocalName().equals("lst1")) {
+ assertTrue(node instanceof CompositeNode);
+ lst1 = (CompositeNode) node;
+ }
+ }
+
+ assertNotNull(lf1);
+ assertNotNull(lflst1_1);
+ assertNotNull(lflst1_2);
+ assertNotNull(lst1);
+
+ assertEquals("", lf1.getValue());
+ assertEquals("", lflst1_1.getValue());
+ assertEquals("", lflst1_2.getValue());
+ assertEquals(1, lst1.getChildren().size());
+ assertEquals("lf11", lst1.getChildren().get(0).getNodeType().getLocalName());
+
+ assertTrue(lst1.getChildren().get(0) instanceof SimpleNode<?>);
+ assertEquals("", lst1.getChildren().get(0).getValue());
+
+ }
+
+ private void verifyCommonPartAOfXml(CompositeNode compNode, String suf, String nameSpace) {
+ SimpleNode<?> lf1suf = null;
+ SimpleNode<?> lflst1suf_1 = null;
+ SimpleNode<?> lflst1suf_2 = null;
+ SimpleNode<?> lflst1suf_3 = null;
+ CompositeNode cont1suf = null;
+ CompositeNode lst1suf = null;
+
+ int lflstCount = 0;
+
+ for (Node<?> node : compNode.getChildren()) {
+ String localName = node.getNodeType().getLocalName();
+ if (localName.equals("lf1" + suf)) {
+ assertTrue(node instanceof SimpleNode<?>);
+ lf1suf = (SimpleNode<?>) node;
+ } else if (localName.equals("lflst1" + suf)) {
+ assertTrue(node instanceof SimpleNode<?>);
+ switch (lflstCount++) {
+ case 0:
+ lflst1suf_1 = (SimpleNode<?>) node;
+ break;
+ case 1:
+ lflst1suf_2 = (SimpleNode<?>) node;
+ break;
+ case 2:
+ lflst1suf_3 = (SimpleNode<?>) node;
+ break;
+ }
+ } else if (localName.equals("lst1" + suf)) {
+ assertTrue(node instanceof CompositeNode);
+ lst1suf = (CompositeNode) node;
+ } else if (localName.equals("cont1" + suf)) {
+ assertTrue(node instanceof CompositeNode);
+ cont1suf = (CompositeNode) node;
+ }
+ assertEquals(nameSpace, node.getNodeType().getNamespace().toString());
+ }
+
+ assertNotNull(lf1suf);
+ assertNotNull(lflst1suf_1);
+ assertNotNull(lflst1suf_2);
+ assertNotNull(lflst1suf_3);
+ assertNotNull(lst1suf);
+ assertNotNull(cont1suf);
+
+ assertEquals("str0", lf1suf.getValue());
+ assertEquals("121", lflst1suf_1.getValue());
+ assertEquals("131", lflst1suf_2.getValue());
+ assertEquals("str1", lflst1suf_3.getValue());
+
+ assertEquals(1, lst1suf.getChildren().size());
+
+ assertTrue(lst1suf.getChildren().get(0) instanceof SimpleNode<?>);
+ SimpleNode<?> lst11_lf11 = (SimpleNode<?>) lst1suf.getChildren().get(0);
+ assertEquals(nameSpace, lst11_lf11.getNodeType().getNamespace().toString());
+ assertEquals("lf11" + suf, lst11_lf11.getNodeType().getLocalName());
+ assertEquals("str2", lst11_lf11.getValue());
+
+ assertTrue(cont1suf.getChildren().get(0) instanceof SimpleNode<?>);
+ SimpleNode<?> cont1_lf11 = (SimpleNode<?>) cont1suf.getChildren().get(0);
+ assertEquals(nameSpace, cont1_lf11.getNodeType().getNamespace().toString());
+ assertEquals("lf11" + suf, cont1_lf11.getNodeType().getLocalName());
+ assertEquals("100", cont1_lf11.getValue());
+ }
+
+ private CompositeNode compositeContainerFromXml(String xmlPath, boolean dummyNamespaces) {
+ XmlToCompositeNodeProvider xmlToCompositeNodeProvider = XmlToCompositeNodeProvider.INSTANCE;
+ try {
+ InputStream xmlStream = FromXmlToCompositeNodeTest.class.getResourceAsStream(xmlPath);
+ CompositeNode compositeNode = xmlToCompositeNodeProvider.readFrom(null, null, null, null, null, xmlStream);
+ if (dummyNamespaces) {
+ try {
+ TestUtils.addDummyNamespaceToAllNodes((CompositeNodeWrapper) compositeNode);
+ return ((CompositeNodeWrapper) compositeNode).unwrap(null);
+ } catch (URISyntaxException e) {
+ LOG.error(e.getMessage());
+ assertTrue(e.getMessage(), false);
+ }
+ }
+ return compositeNode;
+
+ } catch (WebApplicationException | IOException e) {
+ LOG.error(e.getMessage());
+ assertTrue(false);
+ }
+ return null;
+ }
+
+}
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.io.*;
import java.net.*;
import java.sql.Date;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.concurrent.Future;
import javax.ws.rs.WebApplicationException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
-import org.opendaylight.controller.sal.rest.impl.*;
-import org.opendaylight.controller.sal.restconf.impl.StructuredData;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
+import org.opendaylight.controller.sal.restconf.impl.*;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.*;
-import org.opendaylight.yangtools.yang.data.impl.*;
+import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
import org.opendaylight.yangtools.yang.model.api.*;
import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.slf4j.*;
import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Preconditions;
final class TestUtils {
}
return (CompositeNode) dataTree;
}
+
+ public static Document loadDocumentFrom(InputStream inputStream) {
+ try {
+ DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+ return docBuilder.parse(inputStream);
+ } catch (SAXException | IOException | ParserConfigurationException e) {
+ logger.error("Error during loading Document from XML", e);
+ return null;
+ }
+ }
public static String getDocumentInPrintableForm(Document doc) {
+ Preconditions.checkNotNull(doc);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
TransformerFactory tf = TransformerFactory.newInstance();
static QName buildQName(String name) {
return buildQName(name, "", null);
}
+
+ static void supplementNamespace(DataSchemaNode dataSchemaNode, CompositeNode compositeNode) {
+ RestconfImpl restconf = RestconfImpl.getInstance();
+
+ InstanceIdWithSchemaNode instIdAndSchema = new InstanceIdWithSchemaNode(mock(InstanceIdentifier.class),
+ dataSchemaNode);
+
+ ControllerContext controllerContext = mock(ControllerContext.class);
+ BrokerFacade broker = mock(BrokerFacade.class);
+
+ RpcResult<TransactionStatus> rpcResult = DummyRpcResult.builder().result(TransactionStatus.COMMITED).build();
+ Future<RpcResult<TransactionStatus>> future = DummyFuture.builder().rpcResult(rpcResult).build();
+ when(controllerContext.toInstanceIdentifier(any(String.class))).thenReturn(instIdAndSchema);
+ when(broker.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(future);
+
+ restconf.setControllerContext(controllerContext);
+ restconf.setBroker(broker);
+
+ // method is called only because it contains call of method which
+ // supplement namespaces to compositeNode
+ restconf.createConfigurationData("something", compositeNode);
+ }
+
+ static DataSchemaNode obtainSchemaFromYang(String yangFolder) throws FileNotFoundException {
+ return obtainSchemaFromYang(yangFolder, null);
+ }
+
+ static DataSchemaNode obtainSchemaFromYang(String yangFolder, String moduleName) throws FileNotFoundException {
+ Set<Module> modules = null;
+ modules = TestUtils.loadModules(ToJsonBasicDataTypesTest.class.getResource(yangFolder).getPath());
+
+ if (modules == null) {
+ return null;
+ }
+ if (modules.size() < 1) {
+ return null;
+ }
+
+ Module moduleRes = null;
+ if (modules.size() > 1) {
+ if (moduleName == null) {
+ return null;
+ } else {
+ for (Module module: modules) {
+ if (module.getName().equals(moduleName)) {
+ moduleRes = module;
+ }
+ }
+ if (moduleRes == null) {
+ return null;
+ }
+ }
+ } else {
+ moduleRes = modules.iterator().next();
+ }
+
+ if (moduleRes.getChildNodes() == null) {
+ return null;
+ }
+
+ if (moduleRes.getChildNodes().size() != 1) {
+ return null;
+ }
+ DataSchemaNode dataSchemaNode = moduleRes.getChildNodes().iterator().next();
+ return dataSchemaNode;
+
+ }
+
+ static void addDummyNamespaceToAllNodes(NodeWrapper<?> wrappedNode) throws URISyntaxException {
+ wrappedNode.setNamespace(new URI(""));
+ if (wrappedNode instanceof CompositeNodeWrapper) {
+ for (NodeWrapper<?> childNodeWrapper : ((CompositeNodeWrapper) wrappedNode).getValues()) {
+ addDummyNamespaceToAllNodes(childNodeWrapper);
+ }
+ }
+ }
+
}
boolean lfbinaryChecked = false;
// boolean lfref1Checked = false;
boolean lfemptyChecked = false;
-
+ boolean lfstr1Checked = false;
+
while (jReader.hasNext()) {
String keyName = jReader.nextName();
JsonToken peek = null;
assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
assertEquals("lfstr", jReader.nextString());
lfstrChecked = true;
+ } else if (keyName.equals("lfstr1")) {
+ assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+ assertEquals("", jReader.nextString());
+ lfstr1Checked = true;
} else if (keyName.equals("lfbool1")) {
assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek);
assertEquals(true, jReader.nextBoolean());
assertTrue("lfbool1 wasn't checked", lfbool1Checked);
assertTrue("lfbool2 wasn't checked", lfbool2Checked);
assertTrue("lfstr wasn't checked", lfstrChecked);
+ assertTrue("lfstr1 wasn't checked", lfstr1Checked);
assertTrue("lfbinary wasn't checked", lfbinaryChecked);
assertTrue("lfempty wasn't checked", lfemptyChecked);
// assertTrue("lfref1 wasn't checked", lfref1Checked);
package org.opendaylight.controller.sal.restconf.impl.test;
-import static org.mockito.Mockito.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.io.FileNotFoundException;
-import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.MediaTypes;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
import com.google.common.base.Charsets;
private static ControllerContext controllerContext;
private static BrokerFacade brokerFacade;
private static RestconfImpl restconfImpl;
+ private static final MediaType MEDIA_TYPE = new MediaType("application", "vnd.yang.api+xml");
@BeforeClass
- public static void init() {
- Set<Module> allModules = null;
- try {
- allModules = TestUtils.loadModules(RestconfImplTest.class.getResource("/full-versions/yangs").getPath());
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
+ public static void init() throws FileNotFoundException {
+ Set<Module> allModules = TestUtils.loadModules(RestconfImplTest.class.getResource("/full-versions/yangs").getPath());
SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
controllerContext = ControllerContext.getInstance();
controllerContext.setSchemas(schemaContext);
restconfImpl.setControllerContext(controllerContext);
}
-// @Before
+ @Before
public void logs() {
List<LogRecord> loggedRecords = getLoggedRecords();
for (LogRecord l : loggedRecords) {
}
@Test
- public void testStructuredDataToXmlProvider() throws FileNotFoundException {
- URI uri = null;
- try {
- uri = new URI("/datastore/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
- } catch (UnsupportedEncodingException | URISyntaxException e) {
- e.printStackTrace();
- }
+ public void testStructuredDataToXmlProvider() throws FileNotFoundException, UnsupportedEncodingException {
+ String uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
- Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).get();
+ Response response = target(uri).request(MEDIA_TYPE).get();
assertEquals(200, response.getStatus());
}
@Test
- public void testXmlToCompositeNodeProvider() throws ParserConfigurationException, SAXException, IOException {
- URI uri = null;
- try {
- uri = new URI("/operations/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
- } catch (UnsupportedEncodingException | URISyntaxException e) {
- e.printStackTrace();
- }
+ public void testBadFormatXmlToCompositeNodeProvider() throws UnsupportedEncodingException, URISyntaxException {
+ String uri = createUri("/operations/", "ietf-interfaces:interfaces/interface/eth0");
+
+ Response response = target(uri).request(MediaTypes.API + RestconfService.XML).post(
+ Entity.entity("<SimpleNode/>", MEDIA_TYPE));
+ assertEquals(400, response.getStatus());
+
+ response = target(uri).request(MediaTypes.API + RestconfService.XML).post(
+ Entity.entity("<SimpleNode>", MEDIA_TYPE));
+ assertEquals(400, response.getStatus());
+ }
+
+ @Test
+ public void testXmlToCompositeNode404NotFound() throws UnsupportedEncodingException, URISyntaxException {
+ String uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
+
+ when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(null);
+
+ Response response = target(uri).request(MediaTypes.API+RestconfService.XML).get();
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testRpcResultCommitedToStatusCodes() throws UnsupportedEncodingException {
InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
- final CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
- when(brokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(new RpcResult<CompositeNode>() {
-
- @Override
- public boolean isSuccessful() {
- return true;
- }
-
- @Override
- public CompositeNode getResult() {
- return loadedCompositeNode;
- }
-
- @Override
- public Collection<RpcError> getErrors() {
- return null;
- }
- });
+ String xml = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream));
+ Entity<String> entity = Entity.entity(xml, MEDIA_TYPE);
+ RpcResult<TransactionStatus> rpcResult = DummyRpcResult.builder().result(TransactionStatus.COMMITED).build();
+ Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
+ when(brokerFacade.commitOperationalDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
+ when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
+
+ String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
+ Response response = target(uri).request(MEDIA_TYPE).put(entity);
+ assertEquals(200, response.getStatus());
+ response = target(uri).request(MEDIA_TYPE).post(entity);
+ assertEquals(204, response.getStatus());
- DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
- DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
- xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
- Document doc = docBuilder.parse(xmlStream);
+ uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+ response = target(uri).request(MEDIA_TYPE).put(entity);
+ assertEquals(200, response.getStatus());
+ response = target(uri).request(MEDIA_TYPE).post(entity);
+ assertEquals(204, response.getStatus());
- Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).post(Entity.entity(TestUtils.getDocumentInPrintableForm(doc), new MediaType("application","vnd.yang.api+xml")));
+ uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
+ response = target(uri).request(MEDIA_TYPE).put(entity);
assertEquals(200, response.getStatus());
+ response = target(uri).request(MEDIA_TYPE).post(entity);
+ assertEquals(204, response.getStatus());
}
@Test
- public void testXmlToCompositeNodeProviderExceptions() {
- URI uri = null;
- try {
- uri = new URI("/operations/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
- } catch (UnsupportedEncodingException | URISyntaxException e) {
- e.printStackTrace();
- }
+ public void testRpcResultOtherToStatusCodes() throws UnsupportedEncodingException {
+ InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+ String xml = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream));
+ Entity<String> entity = Entity.entity(xml, MEDIA_TYPE);
+ RpcResult<TransactionStatus> rpcResult = DummyRpcResult.builder().result(TransactionStatus.FAILED).build();
+ Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
+ when(brokerFacade.commitOperationalDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
+ when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
- Response response = target(uri.toASCIIString()).request(MediaTypes.API + RestconfService.XML).post(
- Entity.entity("<SimpleNode/>", new MediaType("application", "vnd.yang.api+xml")));
- assertEquals(400, response.getStatus());
+ String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
+ Response response = target(uri).request(MEDIA_TYPE).put(entity);
+ assertEquals(500, response.getStatus());
+ response = target(uri).request(MEDIA_TYPE).post(entity);
+ assertEquals(500, response.getStatus());
- response = target(uri.toASCIIString()).request(MediaTypes.API + RestconfService.XML).post(
- Entity.entity("<SimpleNode>", new MediaType("application", "vnd.yang.api+xml")));
- assertEquals(400, response.getStatus());
+ uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+ response = target(uri).request(MEDIA_TYPE).put(entity);
+ assertEquals(500, response.getStatus());
+ response = target(uri).request(MEDIA_TYPE).post(entity);
+ assertEquals(500, response.getStatus());
+
+ uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
+ response = target(uri).request(MEDIA_TYPE).put(entity);
+ assertEquals(500, response.getStatus());
+ response = target(uri).request(MEDIA_TYPE).post(entity);
+ assertEquals(500, response.getStatus());
+ }
+
+ private String createUri(String prefix, String encodedPart) throws UnsupportedEncodingException {
+ return URI.create(prefix + URLEncoder.encode(encodedPart, Charsets.US_ASCII.name()).toString()).toASCIIString();
}
@Override
--- /dev/null
+{
+ "cont": {
+ "lf":[null]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "cont": {
+ "lflst1":[],
+ "lflst2":[45]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "cont": {
+ "lf":
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "lst":[
+ {
+ "lst1": [
+ {
+ "lf11":"lf11_1"
+ },
+ {
+ "lflst11":[
+ 45
+ ]
+ },
+ {
+ "cont11":{
+ }
+ },
+ {
+ "lst11":[
+ {
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "cont": {
+ "lflst1":[45,55,66]
+ }
+}
\ No newline at end of file
--- /dev/null
+module simple-container-yang {
+ namespace "simple:container:yang";
+
+ prefix "smpdtp";
+ revision 2013-11-12 {
+ }
+
+ container cont {
+ container cont1 {
+ }
+ list lst1 {
+ }
+ leaf-list lflst1 {
+ type string;
+ }
+ leaf lf1 {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "cont":{
+ "cont1":{
+ },
+ "lst1": [
+ {
+ }
+ ],
+ "lflst1":[
+ "lflst1_1",
+ "lflst1_2"
+ ],
+ "lf1":"lf1"
+ }
+}
\ No newline at end of file
--- /dev/null
+module simple-list-yang1 {
+ namespace "simple:list:yang1";
+
+ prefix "smplstyg";
+ revision 2013-11-12 {
+ }
+
+ list lst {
+ container cont1 {
+ }
+ list lst1 {
+ }
+ leaf-list lflst1 {
+ type string;
+ }
+ leaf lf1 {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module simple-list-yang2 {
+ namespace "simple:list:yang2";
+
+ prefix "smplstyg";
+ revision 2013-11-12 {
+ }
+
+ list lst {
+ container cont1 {
+ }
+ list lst1 {
+ }
+ leaf-list lflst1 {
+ type string;
+ }
+ leaf lf1 {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "lst":[
+ {
+ "cont1":{
+ },
+ "lst1": [
+ {
+ }
+ ],
+ "lflst1":[
+ "lflst1_1",
+ "lflst1_2"
+ ],
+ "lf1":"lf1"
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+
+ "lst":[
+ {
+ },
+ {
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+
+ "cont": {
+ },
+ "lst":[
+ {
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "lf":"hello"
+}
\ No newline at end of file
--- /dev/null
+module data-container-yang {
+ namespace "data:container:yang";
+
+ prefix "dtconyg";
+ revision 2013-11-19 {
+ }
+
+ container cont {
+ leaf lf1 {
+ type string;
+ }
+ leaf-list lflst1 {
+ type string;
+ }
+ list lst1 {
+ leaf lf11 {
+ type string;
+ }
+ }
+ container cont1 {
+ leaf lf11 {
+ type uint8;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<cont>
+ <lf1>str0</lf1>
+ <lflst1>121</lflst1>
+ <lflst1>131</lflst1>
+ <lflst1>str1</lflst1>
+ <lst1>
+ <lf11>str2</lf11>
+ </lst1>
+ <cont1>
+ <lf11>100</lf11>
+ </cont1>
+</cont>
--- /dev/null
+module data-container-yang {
+ namespace "data:container:yang";
+
+ prefix "dtconyg";
+ revision 2013-11-19 {
+ }
+
+ container cont {
+ list lst1 {
+ leaf lf11 {
+ type string;
+ }
+ leaf-list lflst11 {
+ type string;
+ }
+ list lst11 {
+ leaf lf111 {
+ type string;
+ }
+ }
+ container cont11 {
+ leaf lf111 {
+ type uint8;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module data-list-yang {
+ namespace "data:list:yang";
+
+ prefix "dtlstyg";
+
+ import data-container-yang {
+ prefix "dtconyg";
+ revision-date 2013-11-19;
+ }
+
+
+ revision 2013-11-19 {
+ }
+
+
+
+ augment "/dtconyg:cont" {
+ leaf lf1 {
+ type string;
+ }
+ }
+}
--- /dev/null
+<cont>
+ <lst1>
+ <lf11>str0</lf11>
+ <lflst11>121</lflst11>
+ <lflst11>131</lflst11>
+ <lflst11>str1</lflst11>
+ <lst11>
+ <lf111>str2</lf111>
+ </lst11>
+ <cont11>
+ <lf111>100</lf111>
+ </cont11>
+ </lst1>
+ <lst1>
+ <lflst11>221</lflst11>
+ <cont11>
+ <lf111>100</lf111>
+ </cont11>
+ </lst1>
+ <lf1>lf1</lf1>
+</cont>
--- /dev/null
+<cont>
+ <lf1></lf1>
+ <lflst1></lflst1>
+ <lflst1></lflst1>
+ <lst1>
+ <lf11></lf11>
+ </lst1>
+</cont>
leaf lfstr {
type string;
}
+ leaf lfstr1 {
+ type string;
+ }
leaf lfbool1 {
type boolean;
}
<lfnuint16Max>65535</lfnuint16Max>
<lfnuint32Max>4294967295</lfnuint32Max>
<lfstr>lfstr</lfstr>
+ <lfstr1></lfstr1>
<lfbool1>true</lfbool1>
<lfbool2>false</lfbool2>
<lfbool3>bla</lfbool3>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>${bundle.plugin.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.connector.remoterpc.api;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
-import java.util.EventListener;
-
-public interface RouteChangeListener extends EventListener {
-
- public void onRouteChanged(RouteChange<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String> change);
-}
</instructions>
</configuration>
</plugin>
- <plugin>
- <groupId>org.eclipse.xtend</groupId>
- <artifactId>xtend-maven-plugin</artifactId>
- </plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
+/*
+ * Copyright IBM Corporation, 2013. 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.statistics.manager;
import java.util.List;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.desc.response.GroupDescStats;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.stats.response.GroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.nodes.node.GroupFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.desc.stats.reply.GroupDescStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.meter.config.response.MeterConfigStats;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.meter.stats.response.MeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.config.stats.reply.MeterConfigStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats;
public class NodeStatistics {
private NodeRef targetNode;
- private List<GroupStatistics> groupStatistics;
+ private List<GroupStats> groupStatistics;
- private List<MeterStatistics> meterStatistics;
+ private List<MeterStats> meterStatistics;
private List<GroupDescStats> groupDescStats;
private List<MeterConfigStats> meterConfigStats;
- private List<org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.features.GroupFeatures> groupFeatures;
+ private GroupFeatures groupFeatures;
- private List<org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.features.MeterFeatures> meterFeatures;
+ private MeterFeatures meterFeatures;
public NodeStatistics(){
this.targetNode = targetNode;
}
- public List<GroupStatistics> getGroupStatistics() {
+ public List<GroupStats> getGroupStatistics() {
return groupStatistics;
}
- public void setGroupStatistics(List<GroupStatistics> groupStatistics) {
+ public void setGroupStatistics(List<GroupStats> groupStatistics) {
this.groupStatistics = groupStatistics;
}
- public List<MeterStatistics> getMeterStatistics() {
+ public List<MeterStats> getMeterStatistics() {
return meterStatistics;
}
- public void setMeterStatistics(List<MeterStatistics> meterStatistics) {
+ public void setMeterStatistics(List<MeterStats> meterStatistics) {
this.meterStatistics = meterStatistics;
}
this.meterConfigStats = meterConfigStats;
}
- public List<org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.features.GroupFeatures> getGroupFeatures() {
+ public GroupFeatures getGroupFeatures() {
return groupFeatures;
}
- public void setGroupFeatures(
- List<org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.features.GroupFeatures> groupFeatures) {
+ public void setGroupFeatures(GroupFeatures groupFeatures) {
this.groupFeatures = groupFeatures;
}
- public List<org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.features.MeterFeatures> getMeterFeatures() {
+ public MeterFeatures getMeterFeatures() {
return meterFeatures;
}
- public void setMeterFeatures(
- List<org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.features.MeterFeatures> meterFeatures) {
+ public void setMeterFeatures(MeterFeatures meterFeatures) {
this.meterFeatures = meterFeatures;
}
+/*
+ * Copyright IBM Corporation, 2013. 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
+ */
+/*
+ * TODO: Handle multipart messages with following flag true
+ * OFPMPF_REPLY_MORE = 1 << 0
+ * Better accumulate all the messages and update local cache
+ * and configurational data store
+ */
package org.opendaylight.controller.md.statistics.manager;
import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
+/*
+ * Copyright IBM Corporation, 2013. 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.statistics.manager;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetGroupFeaturesInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GetGroupFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.OpendaylightGroupStatisticsService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.GetAllMeterConfigStatisticsInputBuilder;
//Local caching of stats
- private final ConcurrentMap<NodeRef,NodeStatistics> statisticsCache =
- new ConcurrentHashMap<NodeRef,NodeStatistics>();
+ private final ConcurrentMap<NodeId,NodeStatistics> statisticsCache =
+ new ConcurrentHashMap<NodeId,NodeStatistics>();
public DataProviderService getDataService() {
return this.dps;
public void run() {
while(true){
try {
+ spLogger.info("Statistics requester thread started with timer interval : {}",5000);
+
statsRequestSender();
Thread.sleep(5000);
}catch (Exception e){
- spLogger.error("Exception occurred while sending stats request : {}",e.getMessage());
- e.printStackTrace();
+ spLogger.error("Exception occurred while sending stats request : {}",e);
}
}
}
private void statsRequestSender(){
//Need to call API to receive all the nodes connected to controller.
-
- List<NodeRef> targetNodes = new ArrayList<NodeRef>();
-
- for (NodeRef targetNode : targetNodes){
-
- sendAllGroupStatisticsRequest(targetNode);
-
- sendAllMeterStatisticsRequest(targetNode);
+ List<Node> targetNodes = getAllConnectedNodes();
- //We need to add check, so see if groups/meters are supported
- //by the target node.
- sendGroupDescriptionRequest(targetNode);
-
- sendGroupFeaturesRequest(targetNode);
-
- sendMeterConfigStatisticsRequest(targetNode);
+ for (Node targetNode : targetNodes){
+ spLogger.info("Send request for stats collection to node : {})",targetNode.getId());
- sendMeterFeaturesRequest(targetNode);
+ //We need to add check, so see if groups/meters are supported
+ //by the target node. Below check doesn't look good.
+ if(targetNode.getId().getValue().contains("openflow:")){
+ sendAllGroupStatisticsRequest(targetNode);
+
+ sendAllMeterStatisticsRequest(targetNode);
+
+ sendGroupDescriptionRequest(targetNode);
+
+ sendGroupFeaturesRequest(targetNode);
+
+ sendMeterConfigStatisticsRequest(targetNode);
+
+ sendMeterFeaturesRequest(targetNode);
+ }
}
}
- private void sendAllGroupStatisticsRequest(NodeRef targetNode){
+ private void sendAllGroupStatisticsRequest(Node targetNode){
- GetAllGroupStatisticsInputBuilder input = new GetAllGroupStatisticsInputBuilder();
+ final GetAllGroupStatisticsInputBuilder input = new GetAllGroupStatisticsInputBuilder();
- input.setNode(targetNode);
+ input.setId(targetNode.getId());
Future<RpcResult<GetAllGroupStatisticsOutput>> response =
groupStatsService.getAllGroupStatistics(input.build());
}
- private void sendGroupDescriptionRequest(NodeRef targetNode){
- GetGroupDescriptionInputBuilder input = new GetGroupDescriptionInputBuilder();
+ private void sendGroupDescriptionRequest(Node targetNode){
+ final GetGroupDescriptionInputBuilder input = new GetGroupDescriptionInputBuilder();
- input.setNode(targetNode);
+ input.setId(targetNode.getId());
Future<RpcResult<GetGroupDescriptionOutput>> response =
groupStatsService.getGroupDescription(input.build());
}
- private void sendGroupFeaturesRequest(NodeRef targetNode){
+ private void sendGroupFeaturesRequest(Node targetNode){
GetGroupFeaturesInputBuilder input = new GetGroupFeaturesInputBuilder();
- input.setNode(targetNode);
+ input.setId(targetNode.getId());
Future<RpcResult<GetGroupFeaturesOutput>> response =
groupStatsService.getGroupFeatures(input.build());
}
- private void sendAllMeterStatisticsRequest(NodeRef targenetNode){
+ private void sendAllMeterStatisticsRequest(Node targetNode){
GetAllMeterStatisticsInputBuilder input = new GetAllMeterStatisticsInputBuilder();
- input.setNode(targenetNode);
+ input.setId(targetNode.getId());
Future<RpcResult<GetAllMeterStatisticsOutput>> response =
meterStatsService.getAllMeterStatistics(input.build());
}
- private void sendMeterConfigStatisticsRequest(NodeRef targetNode){
+ private void sendMeterConfigStatisticsRequest(Node targetNode){
GetAllMeterConfigStatisticsInputBuilder input = new GetAllMeterConfigStatisticsInputBuilder();
- input.setNode(targetNode);
+ input.setId(targetNode.getId());
Future<RpcResult<GetAllMeterConfigStatisticsOutput>> response =
meterStatsService.getAllMeterConfigStatistics(input.build());
}
- private void sendMeterFeaturesRequest(NodeRef targetNode){
+ private void sendMeterFeaturesRequest(Node targetNode){
GetMeterFeaturesInputBuilder input = new GetMeterFeaturesInputBuilder();
- input.setNode(targetNode);
+ input.setId(targetNode.getId());
Future<RpcResult<GetMeterFeaturesOutput>> response =
meterStatsService.getMeterFeatures(input.build());
}
- public ConcurrentMap<NodeRef, NodeStatistics> getStatisticsCache() {
+ public ConcurrentMap<NodeId, NodeStatistics> getStatisticsCache() {
return statisticsCache;
}
private List<Node> getAllConnectedNodes(){
Nodes nodes = (Nodes) dps.readOperationalData(nodesIdentifier);
+ spLogger.info("Number of connected nodes : {}",nodes.getNode().size());
return nodes.getNode();
}
+/*
+ * Copyright IBM Corporation, 2013. 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.statistics.manager;
import java.util.concurrent.ConcurrentMap;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupDescRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupDescStatsUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupFeaturesRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupFeaturesUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatsRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupDescStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupDescStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatisticsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.OpendaylightGroupStatisticsListener;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.all.statistics.GroupDescBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.all.statistics.GroupFeaturesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.all.statistics.GroupStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.nodes.node.GroupDescBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.nodes.node.GroupFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.nodes.node.GroupStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterConfigRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterConfigStatsUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterFeaturesRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterFeaturesUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatisticsUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatsRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterConfigStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterConfigStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatisticsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.OpendaylightMeterStatisticsListener;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.meter.all.stats.MeterConfigBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.meter.all.stats.MeterFeaturesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.meter.all.stats.MeterStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterConfigStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterStatisticsBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
public class StatisticsUpdateCommiter implements OpendaylightGroupStatisticsListener,
OpendaylightMeterStatisticsListener {
this.statisticsManager = manager;
}
+
public StatisticsProvider getStatisticsManager(){
return statisticsManager;
}
+
@Override
public void onMeterConfigStatsUpdated(MeterConfigStatsUpdated notification) {
+
//Add statistics to local cache
- ConcurrentMap<NodeRef, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
- if(!cache.containsKey(notification.getNode())){
- cache.put(notification.getNode(), new NodeStatistics());
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
}
- cache.get(notification.getNode()).setMeterConfigStats(notification.getMeterConfigStats());
+ cache.get(notification.getId()).setMeterConfigStats(notification.getMeterConfigStats());
//Publish data to configuration data store
DataModificationTransaction it = this.statisticsManager.startChange();
- MeterConfigRef ref = notification.getMeterConfigId();
-
- MeterConfigBuilder meterConfig = new MeterConfigBuilder();
- meterConfig.setNode(notification.getNode());
- meterConfig.setMeterConfigStats(notification.getMeterConfigStats());
+ NodeKey key = new NodeKey(notification.getId());
+ NodeRef ref = getNodeRef(key);
+
+ final NodeBuilder nodeData = new NodeBuilder();
+ nodeData.setKey(key);
+
+ NodeMeterConfigStatsBuilder meterConfig= new NodeMeterConfigStatsBuilder();
+ MeterConfigStatsBuilder stats = new MeterConfigStatsBuilder();
+ stats.setMeterConfigStats(notification.getMeterConfigStats());
+ meterConfig.setMeterConfigStats(stats.build());
+
+ //Update augmented data
+ nodeData.addAugmentation(NodeMeterConfigStats.class, meterConfig.build());
InstanceIdentifier<? extends Object> refValue = ref.getValue();
- it.putRuntimeData(refValue, meterConfig.build());
+ it.putRuntimeData(refValue, nodeData.build());
it.commit();
+
}
@Override
public void onMeterStatisticsUpdated(MeterStatisticsUpdated notification) {
//Add statistics to local cache
- ConcurrentMap<NodeRef, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
- if(!cache.containsKey(notification.getNode())){
- cache.put(notification.getNode(), new NodeStatistics());
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
}
- cache.get(notification.getNode()).setMeterStatistics(notification.getMeterStatistics());
+ cache.get(notification.getId()).setMeterStatistics(notification.getMeterStats());
//Publish data to configuration data store
DataModificationTransaction it = this.statisticsManager.startChange();
- MeterStatsRef ref = notification.getMeterStatsId();
-
- MeterStatsBuilder meterStats = new MeterStatsBuilder();
- meterStats.setNode(notification.getNode());
- meterStats.setMeterStatistics(notification.getMeterStatistics());
+ NodeKey key = new NodeKey(notification.getId());
+ NodeRef ref = getNodeRef(key);
+
+ final NodeBuilder nodeData = new NodeBuilder();
+ nodeData.setKey(key);
+
+ NodeMeterStatisticsBuilder meterStats= new NodeMeterStatisticsBuilder();
+ MeterStatisticsBuilder stats = new MeterStatisticsBuilder();
+ stats.setMeterStats(notification.getMeterStats());
+ meterStats.setMeterStatistics(stats.build());
+
+ //Update augmented data
+ nodeData.addAugmentation(NodeMeterStatistics.class, meterStats.build());
InstanceIdentifier<? extends Object> refValue = ref.getValue();
- it.putRuntimeData(refValue, meterStats.build());
+ it.putRuntimeData(refValue, nodeData.build());
it.commit();
+
}
@Override
public void onGroupDescStatsUpdated(GroupDescStatsUpdated notification) {
//Add statistics to local cache
- ConcurrentMap<NodeRef, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
- if(!cache.containsKey(notification.getNode())){
- cache.put(notification.getNode(), new NodeStatistics());
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
}
- cache.get(notification.getNode()).setGroupDescStats(notification.getGroupDescStats());
+ cache.get(notification.getId()).setGroupDescStats(notification.getGroupDescStats());
//Publish data to configuration data store
DataModificationTransaction it = this.statisticsManager.startChange();
- GroupDescRef ref = notification.getGroupDescId();
-
- GroupDescBuilder descStats = new GroupDescBuilder();
- descStats.setNode(notification.getNode());
- descStats.setGroupDescStats(notification.getGroupDescStats());
+ NodeKey key = new NodeKey(notification.getId());
+ NodeRef ref = getNodeRef(key);
+
+ final NodeBuilder nodeData = new NodeBuilder();
+ nodeData.setKey(key);
+
+ NodeGroupDescStatsBuilder groupDesc= new NodeGroupDescStatsBuilder();
+ GroupDescBuilder stats = new GroupDescBuilder();
+ stats.setGroupDescStats(notification.getGroupDescStats());
+ groupDesc.setGroupDesc(stats.build());
+
+ //Update augmented data
+ nodeData.addAugmentation(NodeGroupDescStats.class, groupDesc.build());
InstanceIdentifier<? extends Object> refValue = ref.getValue();
- it.putRuntimeData(refValue, descStats.build());
+ it.putRuntimeData(refValue, nodeData.build());
it.commit();
+
}
@Override
public void onGroupStatisticsUpdated(GroupStatisticsUpdated notification) {
//Add statistics to local cache
- ConcurrentMap<NodeRef, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
- if(!cache.containsKey(notification.getNode())){
- cache.put(notification.getNode(), new NodeStatistics());
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
}
- cache.get(notification.getNode()).setGroupStatistics(notification.getGroupStatistics());
+ cache.get(notification.getId()).setGroupStatistics(notification.getGroupStats());
//Publish data to configuration data store
+
DataModificationTransaction it = this.statisticsManager.startChange();
- GroupStatsRef ref = notification.getGroupStatsId();
-
- GroupStatsBuilder groupStats = new GroupStatsBuilder();
- groupStats.setNode(notification.getNode());
- groupStats.setGroupStatistics(notification.getGroupStatistics());
+ NodeKey key = new NodeKey(notification.getId());
+ NodeRef ref = getNodeRef(key);
+
+ final NodeBuilder nodeData = new NodeBuilder();
+ nodeData.setKey(key);
+
+ NodeGroupStatisticsBuilder groupStats = new NodeGroupStatisticsBuilder();
+ GroupStatisticsBuilder stats = new GroupStatisticsBuilder();
+ stats.setGroupStats(notification.getGroupStats());
+ groupStats.setGroupStatistics(stats.build());
+
+ //Update augmented data
+ nodeData.addAugmentation(NodeGroupStatistics.class, groupStats.build());
InstanceIdentifier<? extends Object> refValue = ref.getValue();
- it.putRuntimeData(refValue, groupStats.build());
+ it.putRuntimeData(refValue, nodeData.build());
it.commit();
}
+
@Override
public void onMeterFeaturesUpdated(MeterFeaturesUpdated notification) {
//Add statistics to local cache
- ConcurrentMap<NodeRef, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
- if(!cache.containsKey(notification.getNode())){
- cache.put(notification.getNode(), new NodeStatistics());
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
}
- cache.get(notification.getNode()).setMeterFeatures(notification.getMeterFeatures());
+ MeterFeaturesBuilder meterFeature = new MeterFeaturesBuilder();
+ meterFeature.setBandTypes(notification.getBandTypes());
+ meterFeature.setCapabilities(notification.getCapabilities());
+ meterFeature.setMaxBands(notification.getMaxBands());
+ meterFeature.setMaxColor(notification.getMaxColor());
+ meterFeature.setMaxMeter(notification.getMaxMeter());
+
+ cache.get(notification.getId()).setMeterFeatures(meterFeature.build());
//Publish data to configuration data store
DataModificationTransaction it = this.statisticsManager.startChange();
- MeterFeaturesRef ref = notification.getMeterFeaturesId();
-
- MeterFeaturesBuilder meterFeatures = new MeterFeaturesBuilder();
- meterFeatures.setNode(notification.getNode());
- meterFeatures.setMeterFeatures(notification.getMeterFeatures());
+ NodeKey key = new NodeKey(notification.getId());
+ NodeRef ref = getNodeRef(key);
+
+ final NodeBuilder nodeData = new NodeBuilder();
+ nodeData.setKey(key);
+
+ NodeMeterFeaturesBuilder nodeMeterFeatures= new NodeMeterFeaturesBuilder();
+ nodeMeterFeatures.setMeterFeatures(meterFeature.build());
+
+ //Update augmented data
+ nodeData.addAugmentation(NodeMeterFeatures.class, nodeMeterFeatures.build());
InstanceIdentifier<? extends Object> refValue = ref.getValue();
- it.putRuntimeData(refValue, meterFeatures.build());
+ it.putRuntimeData(refValue, nodeData.build());
it.commit();
}
@Override
public void onGroupFeaturesUpdated(GroupFeaturesUpdated notification) {
-
//Add statistics to local cache
- ConcurrentMap<NodeRef, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
- if(!cache.containsKey(notification.getNode())){
- cache.put(notification.getNode(), new NodeStatistics());
+ ConcurrentMap<NodeId, NodeStatistics> cache = this.statisticsManager.getStatisticsCache();
+ if(!cache.containsKey(notification.getId())){
+ cache.put(notification.getId(), new NodeStatistics());
}
- cache.get(notification.getNode()).setGroupFeatures(notification.getGroupFeatures());
+
+ GroupFeaturesBuilder groupFeatures = new GroupFeaturesBuilder();
+ groupFeatures.setActions(notification.getActions());
+ groupFeatures.setCapabilities(notification.getCapabilities());
+ groupFeatures.setGroupType(notification.getGroupType());
+ groupFeatures.setMaxGroups(notification.getMaxGroups());
+ cache.get(notification.getId()).setGroupFeatures(groupFeatures.build());
//Publish data to configuration data store
DataModificationTransaction it = this.statisticsManager.startChange();
- GroupFeaturesRef ref = notification.getGroupFeaturesId();
-
- GroupFeaturesBuilder featuresStats = new GroupFeaturesBuilder();
- featuresStats.setNode(notification.getNode());
- featuresStats.setGroupFeatures(notification.getGroupFeatures());
+ NodeKey key = new NodeKey(notification.getId());
+ NodeRef ref = getNodeRef(key);
+
+ final NodeBuilder nodeData = new NodeBuilder();
+ nodeData.setKey(key);
+
+ NodeGroupFeaturesBuilder nodeGroupFeatures= new NodeGroupFeaturesBuilder();
+ nodeGroupFeatures.setGroupFeatures(groupFeatures.build());
+
+ //Update augmented data
+ nodeData.addAugmentation(NodeGroupFeatures.class, nodeGroupFeatures.build());
InstanceIdentifier<? extends Object> refValue = ref.getValue();
- it.putRuntimeData(refValue, featuresStats.build());
+ it.putRuntimeData(refValue, nodeData.build());
it.commit();
}
+
+ private NodeRef getNodeRef(NodeKey nodeKey){
+ InstanceIdentifierBuilder<?> builder = InstanceIdentifier.builder().node(Nodes.class);
+ return new NodeRef(builder.node(Node.class,nodeKey).toInstance());
+ }
+
}
--- /dev/null
+<?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>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <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>
+ <tag>HEAD</tag>
+ </scm>
+
+ <artifactId>zeromq-routingtable.implementation</artifactId>
+ <version>0.4.1-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${bundle.plugin.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+
+ <Import-Package>
+ javax.xml.bind.annotation,
+ org.opendaylight.controller.sal.core,
+ org.opendaylight.controller.sal.utils,
+ org.opendaylight.controller.sal.packet,
+ org.opendaylight.controller.sal.topology,
+ org.opendaylight.controller.clustering.services,
+ org.opendaylight.controller.md.sal.common.api.data,
+ org.opendaylight.yangtools.yang.binding,
+ org.osgi.service.component,
+ org.slf4j,
+ org.apache.felix.dm,
+ org.apache.commons.lang3.builder,
+ org.apache.commons.lang3.tuple,
+ org.eclipse.osgi.framework.console,
+ org.osgi.framework,
+ javax.transaction,
+ com.google.common.base
+ </Import-Package>
+ <Bundle-Activator>
+ org.opendaylight.controller.sal.connector.remoterpc.impl.Activator
+ </Bundle-Activator>
+ </instructions>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-connector-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.1-SNAPSHOT</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+
+
+ </dependencies>
+</project>
* 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.config.yang.store.api;
+package org.opendaylight.controller.sal.connector.remoterpc.api;
-public interface YangStoreListenerRegistration extends AutoCloseable {
+import java.util.EventListener;
- @Override
- void close();
+public interface RouteChangeListener<I,R> extends EventListener {
+
+
+ void onRouteUpdated(I key, R new_value);
+
+ void onRouteDeleted(I key);
}
public interface RoutingTable<I,R> {
+
+
/**
* Adds a network address for the route. If address for route
* exists, appends the address to the list
*
* @param routeId route identifier
* @param route network address
+ * @throws RoutingTableException for any logical exception
+ * @throws SystemException
*/
- public void addRoute(I routeId, R route);
+ public void addRoute(I routeId, R route) throws RoutingTableException,SystemException;
- /**
+ /**
* Adds a network address for the route. If the route already exists,
- * it throws. This method would be used when registering a global service.
+ * it throws <code>DuplicateRouteException</code>.
+ * This method would be used when registering a global service.
+ *
*
* @param routeId route identifier
* @param route network address
* @throws DuplicateRouteException
+ * @throws RoutingTableException
*/
- public void addGlobalRoute(I routeId, R route) throws DuplicateRouteException;
+ public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException;
+
+
+
/**
* Removes the network address for the route from routing table. If only
*/
public void removeRoute(I routeId, R route);
+
+ /**
+ * Remove the route.
+ * This method would be used when registering a global service.
+ * @param routeId
+ * @throws RoutingTableException
+ * @throws SystemException
+ */
+ public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException;
+
/**
* Returns a set of network addresses associated with this route
* @param routeId
* Returns only one address from the list of network addresses
* associated with the route. The algorithm to determine that
* one address is upto the implementer
- * @param route
+ * @param routeId
* @return
*/
public R getARoute(I routeId);
+ /**
+ *
+ * This will be removed after listeners
+ * have made change on their end to use whiteboard pattern
+ * @deprecated
+ */
+
public void registerRouteChangeListener(RouteChangeListener listener);
- public class DuplicateRouteException extends Exception {}
+ public class DuplicateRouteException extends RoutingTableException {
+ public DuplicateRouteException(String message) {
+ super(message);
+ }
+
+ }
+
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.connector.remoterpc.api;
+
+/**
+ * @author: syedbahm
+ */
+public class RoutingTableException extends Exception {
+
+ /**
+ * Constructs a new exception with {@code null} as its detail message.
+ * The cause is not initialized, and may subsequently be initialized by a
+ * call to {@link #initCause}.
+ */
+ public RoutingTableException() {
+ super();
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message. The
+ * cause is not initialized, and may subsequently be initialized by
+ * a call to {@link #initCause}.
+ *
+ * @param message the detail message. The detail message is saved for
+ * later retrieval by the {@link #getMessage()} method.
+ */
+ public RoutingTableException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message and
+ * cause. <p>Note that the detail message associated with
+ * {@code cause} is <i>not</i> automatically incorporated in
+ * this exception's detail message.
+ *
+ * @param message the detail message (which is saved for later retrieval
+ * by the {@link #getMessage()} method).
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A <tt>null</tt> value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ */
+ public RoutingTableException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+
+
+package org.opendaylight.controller.sal.connector.remoterpc.api;
+
+/**
+ * @author: syedbahm
+ *
+ */
+public class SystemException extends Exception {
+ /**
+ * Constructs a new exception with {@code null} as its detail message.
+ * The cause is not initialized, and may subsequently be initialized by a
+ * call to {@link #initCause}.
+ */
+ public SystemException() {
+ super();
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message. The
+ * cause is not initialized, and may subsequently be initialized by
+ * a call to {@link #initCause}.
+ *
+ * @param message the detail message. The detail message is saved for
+ * later retrieval by the {@link #getMessage()} method.
+ */
+ public SystemException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified detail message and
+ * cause. <p>Note that the detail message associated with
+ * {@code cause} is <i>not</i> automatically incorporated in
+ * this exception's detail message.
+ *
+ * @param message the detail message (which is saved for later retrieval
+ * by the {@link #getMessage()} method).
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A <tt>null</tt> value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ * @since 1.4
+ */
+ public SystemException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.connector.remoterpc.impl;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+/**
+ * @author: syedbahm
+ */
+public class Activator extends ComponentActivatorAbstractBase {
+
+ protected static final Logger logger = LoggerFactory
+ .getLogger(Activator.class);
+ private static final String CACHE_UPDATE_AWARE_REGISTRY_KEY = "cachenames" ;
+
+
+ /**
+ * Method which tells how many Global implementations are
+ * supported by the bundle. This way we can tune the number of
+ * components created. This components will be created ONLY at the
+ * time of bundle startup and will be destroyed only at time of
+ * bundle destruction, this is the major difference with the
+ * implementation retrieved via getImplementations where all of
+ * them are assumed to be in a container!
+ *
+ *
+ * @return The list of implementations the bundle will support,
+ * in Global version
+ */
+
+ @Override
+ protected Object[] getGlobalImplementations(){
+ logger.debug("Calling getGlobalImplementations to return:", RoutingTableImpl.class);
+ return new Object[] {
+ RoutingTableImpl.class
+ };
+ }
+
+ /**
+ * Configure the dependency for a given instance Global
+ *
+ * @param c Component assigned for this instance, this will be
+ * what will be used for configuration
+ * @param imp implementation to be configured
+ *
+ */
+ @Override
+ protected void configureGlobalInstance(Component c, Object imp){
+ if (imp.equals(RoutingTableImpl.class)) {
+ Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
+ Set<String> propSet = new HashSet<String>();
+ propSet.add(RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE);
+ props.put(CACHE_UPDATE_AWARE_REGISTRY_KEY, propSet);
+
+ c.setInterface(new String[] { RoutingTable.class.getName(),ICacheUpdateAware.class.getName() }, props);
+ logger.debug("configureGlobalInstance adding dependency:", IClusterGlobalServices.class);
+
+
+ // RouteChangeListener services will be none or many so the
+ // dependency is optional
+ c.add(createServiceDependency()
+ .setService(RouteChangeListener.class)
+ .setCallbacks("setRouteChangeListener", "unsetRouteChangeListener")
+ .setRequired(false));
+
+ //dependency is required as it provides us the caching support
+ c.add(createServiceDependency().setService(
+ IClusterGlobalServices.class).setCallbacks(
+ "setClusterGlobalServices",
+ "unsetClusterGlobalServices").setRequired(true));
+
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.connector.remoterpc.impl;
+
+import com.google.common.base.Preconditions;
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.CacheListenerAddException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
+import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author: syedbahm
+ */
+public class RoutingTableImpl<I, R> implements RoutingTable<I, R>, ICacheUpdateAware<I, R> {
+ public static final String ROUTING_TABLE_GLOBAL_CACHE = "routing_table_global_cache";
+
+ private Logger log = LoggerFactory.getLogger(RoutingTableImpl.class);
+
+ private IClusterGlobalServices clusterGlobalServices = null;
+ private RoutingTableImpl routingTableInstance = null;
+ private ConcurrentMap routingTableCache = null;
+ private Set<RouteChangeListener> routeChangeListeners = Collections
+ .synchronizedSet(new HashSet<RouteChangeListener>());
+
+ public RoutingTableImpl() {
+ }
+
+ @Override
+ public void addRoute(I routeId, R route) throws RoutingTableException {
+ throw new UnsupportedOperationException(" Not implemented yet!");
+ }
+
+ @Override
+ public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "addGlobalRoute: routeId cannot be null!");
+ Preconditions.checkNotNull(route, "addGlobalRoute: route cannot be null!");
+ try {
+
+ Set<R> existingRoute = null;
+ // ok does the global route is already registered ?
+ if ((existingRoute = getRoutes(routeId)) == null) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("addGlobalRoute: adding a new route with id" + routeId + " and value = "
+ + route);
+ }
+ // lets start a transaction
+ clusterGlobalServices.tbegin();
+ Set<R> routes = new HashSet<R>();
+ routes.add(route);
+ routingTableCache.put(routeId, routes);
+ clusterGlobalServices.tcommit();
+ } else {
+ throw new DuplicateRouteException(" There is already existing route " + existingRoute);
+ }
+
+ } catch (NotSupportedException e) {
+ throw new RoutingTableException("Transaction error - while trying to create route id="
+ + routeId + "with route" + route, e);
+ } catch (HeuristicRollbackException e) {
+ throw new RoutingTableException("Transaction error - while trying to create route id="
+ + routeId + "with route" + route, e);
+ } catch (RollbackException e) {
+ throw new RoutingTableException("Transaction error - while trying to create route id="
+ + routeId + "with route" + route, e);
+ } catch (HeuristicMixedException e) {
+ throw new RoutingTableException("Transaction error - while trying to create route id="
+ + routeId + "with route" + route, e);
+ } catch (javax.transaction.SystemException e) {
+ throw new SystemException("System error occurred - while trying to create with value", e);
+ }
+
+ }
+
+ @Override
+ public void removeRoute(I routeId, R route) {
+ throw new UnsupportedOperationException("Not implemented yet!");
+ }
+
+ @Override
+ public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("removeGlobalRoute: removing a new route with id" + routeId);
+ }
+ // lets start a transaction
+ clusterGlobalServices.tbegin();
+
+ routingTableCache.remove(routeId);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (HeuristicRollbackException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (RollbackException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (HeuristicMixedException e) {
+ throw new RoutingTableException("Transaction error - while trying to remove route id="
+ + routeId, e);
+ } catch (javax.transaction.SystemException e) {
+ throw new SystemException("System error occurred - while trying to remove with value", e);
+ }
+ }
+
+ @Override
+ public Set<R> getRoutes(I routeId) {
+
+ // Note: currently works for global routes only wherein there is just single
+ // route
+ Preconditions.checkNotNull(routeId, "getARoute: routeId cannot be null!");
+ return (Set<R>) routingTableCache.get(routeId);
+ }
+
+ @Override
+ public R getARoute(I routeId) {
+ throw new UnsupportedOperationException("Not implemented yet!");
+ }
+
+ /**
+ * @deprecated doesn't do anything will be removed once listeners used
+ * whiteboard pattern Registers listener for sending any change
+ * notification
+ * @param listener
+ */
+ @Override
+ public void registerRouteChangeListener(RouteChangeListener listener) {
+
+ }
+
+ public void setRouteChangeListener(RouteChangeListener rcl) {
+ if(rcl != null){
+ routeChangeListeners.add(rcl);
+ }else{
+ log.warn("setRouteChangeListener called with null listener");
+ }
+ }
+
+ public void unSetRouteChangeListener(RouteChangeListener rcl) {
+ if(rcl != null){
+ routeChangeListeners.remove(rcl);
+ }else{
+ log.warn("unSetRouteChangeListener called with null listener");
+ }
+ }
+
+ /**
+ * Returning the set of route change listeners for Unit testing Note: the
+ * package scope is default
+ *
+ * @return List of registered RouteChangeListener<I,R> listeners
+ */
+ Set<RouteChangeListener> getRegisteredRouteChangeListeners() {
+ return routeChangeListeners;
+ }
+
+ public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
+ this.clusterGlobalServices = clusterGlobalServices;
+ }
+
+ public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
+ if((clusterGlobalServices != null ) && (this.clusterGlobalServices.equals(clusterGlobalServices))){
+ this.clusterGlobalServices = null;
+ }
+ }
+
+ /**
+ * Creates the Routing Table clustered global services cache
+ *
+ * @throws CacheExistException
+ * -- cluster global services exception when cache exist
+ * @throws CacheConfigException
+ * -- cluster global services exception during cache config
+ * @throws CacheListenerAddException
+ * -- cluster global services exception during adding of listener
+ */
+
+ void createRoutingTableCache() throws CacheExistException, CacheConfigException,
+ CacheListenerAddException {
+ // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
+ // should be caching?
+
+ // let us check here if the cache already exists -- if so don't create
+ if (!clusterGlobalServices.existCache(ROUTING_TABLE_GLOBAL_CACHE)) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("createRoutingTableCache: creating a new routing table cache "
+ + ROUTING_TABLE_GLOBAL_CACHE);
+ }
+ routingTableCache = clusterGlobalServices.createCache(ROUTING_TABLE_GLOBAL_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("createRoutingTableCache: found existing routing table cache "
+ + ROUTING_TABLE_GLOBAL_CACHE);
+ }
+ routingTableCache = clusterGlobalServices.getCache(ROUTING_TABLE_GLOBAL_CACHE);
+ }
+
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init(Component c) {
+ try {
+
+ createRoutingTableCache();
+ } catch (CacheExistException e) {
+ throw new IllegalStateException("could not construct routing table cache");
+ } catch (CacheConfigException e) {
+ throw new IllegalStateException("could not construct routing table cache");
+ } catch (CacheListenerAddException e) {
+ throw new IllegalStateException("could not construct routing table cache");
+ }
+ }
+
+ /**
+ * Get routing table method is useful for unit testing <note>It has package
+ * scope</note>
+ */
+ ConcurrentMap getRoutingTableCache() {
+ return this.routingTableCache;
+ }
+
+ /**
+ * Invoked when a new entry is available in the cache, the key is only
+ * provided, the value will come as an entryUpdate invocation
+ *
+ * @param key
+ * Key for the entry just created
+ * @param cacheName
+ * name of the cache for which update has been received
+ * @param originLocal
+ * true if the event is generated from this node
+ */
+ @Override
+ public void entryCreated(I key, String cacheName, boolean originLocal) {
+ // TBD: do we require this.
+ if (log.isDebugEnabled()) {
+ log.debug("RoutingTableUpdates: entryCreated routeId = " + key + " cacheName=" + cacheName);
+ }
+ }
+
+ /**
+ * Called anytime a given entry is updated
+ *
+ * @param key
+ * Key for the entry modified
+ * @param new_value
+ * the new value the key will have
+ * @param cacheName
+ * name of the cache for which update has been received
+ * @param originLocal
+ * true if the event is generated from this node
+ */
+ @Override
+ public void entryUpdated(I key, R new_value, String cacheName, boolean originLocal) {
+ if (log.isDebugEnabled()) {
+ log.debug("RoutingTableUpdates: entryUpdated routeId = " + key + ",value = " + new_value
+ + " ,cacheName=" + cacheName + " originLocal="+originLocal);
+ }
+ if (!originLocal) {
+ for (RouteChangeListener rcl : routeChangeListeners) {
+ rcl.onRouteUpdated(key, new_value);
+ }
+ }
+ }
+
+ /**
+ * Called anytime a given key is removed from the ConcurrentHashMap we are
+ * listening to.
+ *
+ * @param key
+ * Key of the entry removed
+ * @param cacheName
+ * name of the cache for which update has been received
+ * @param originLocal
+ * true if the event is generated from this node
+ */
+ @Override
+ public void entryDeleted(I key, String cacheName, boolean originLocal) {
+ if (log.isDebugEnabled()) {
+ log.debug("RoutingTableUpdates: entryUpdated routeId = " + key + " local = " + originLocal
+ + " cacheName=" + cacheName + " originLocal="+originLocal);
+ }
+ if (!originLocal) {
+ for (RouteChangeListener rcl : routeChangeListeners) {
+ rcl.onRouteDeleted(key);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.connector.remoterpc.impl;
+
+import junit.framework.Assert;
+import org.apache.felix.dm.Component;
+import org.junit.Test;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+import java.net.URI;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author: syedbahm
+ */
+public class RoutingTableImplTest {
+
+ private IClusterGlobalServices ics = mock(IClusterGlobalServices.class);
+ private RoutingTableImpl rti = new RoutingTableImpl();
+
+ private final URI namespace = URI.create("http://cisco.com/example");
+ private final QName QNAME = new QName(namespace,"global");
+
+ ConcurrentMap concurrentMapMock = mock(ConcurrentMap.class);
+
+
+ @Test
+ public void testAddGlobalRoute() throws Exception {
+ ConcurrentMap concurrentMap = createRoutingTableCache();
+
+ Assert.assertNotNull(concurrentMap);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
+ InstanceIdentifier identifier = mock(InstanceIdentifier.class);
+ when(routeIdentifier.getType()).thenReturn(QNAME);
+ when(routeIdentifier.getRoute()).thenReturn(identifier);
+
+ rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+
+ Set<String> globalService = new HashSet<String>();
+ globalService.add("172.27.12.1:5000");
+
+ when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
+ ConcurrentMap latestCache = rti.getRoutingTableCache();
+
+ Assert.assertEquals(concurrentMap,latestCache);
+
+ Set<String> servicesGlobal = (Set<String>)latestCache.get(routeIdentifier);
+ Assert.assertEquals(servicesGlobal.size(),1);
+
+ Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+
+ }
+
+ @Test
+ public void testGetRoutes() throws Exception {
+ ConcurrentMap concurrentMap = createRoutingTableCache();
+
+ Assert.assertNotNull(concurrentMap);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
+ InstanceIdentifier identifier = mock(InstanceIdentifier.class);
+ when(routeIdentifier.getContext()).thenReturn(QNAME);
+ when(routeIdentifier.getRoute()).thenReturn(identifier);
+
+ rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+
+ Set<String> globalService = new HashSet<String>();
+ globalService.add("172.27.12.1:5000");
+
+ when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
+ ConcurrentMap latestCache = rti.getRoutingTableCache();
+
+ Assert.assertEquals(concurrentMap,latestCache);
+
+ Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+
+
+ Assert.assertEquals(servicesGlobal.size(),1);
+
+ Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+
+
+
+ }
+ @Test
+ public void testRegisterRouteChangeListener() throws Exception {
+ Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0);
+ rti.registerRouteChangeListener(new RouteChangeListenerImpl());
+
+ Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0); //old should not work
+ //what about the new approach - using whiteboard pattern
+ rti.setRouteChangeListener(new RouteChangeListenerImpl());
+
+ Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),1); //should not work
+
+
+ }
+ @Test
+ public void testRemoveGlobalRoute()throws Exception {
+
+ ConcurrentMap concurrentMap = createRoutingTableCache();
+
+ Assert.assertNotNull(concurrentMap);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = mock(RpcRouter.RouteIdentifier.class);
+ InstanceIdentifier identifier = mock(InstanceIdentifier.class);
+ when(routeIdentifier.getContext()).thenReturn(QNAME);
+ when(routeIdentifier.getRoute()).thenReturn(identifier);
+
+ rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+
+ Set<String> globalService = new HashSet<String>();
+ globalService.add("172.27.12.1:5000");
+
+ when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
+ ConcurrentMap latestCache = rti.getRoutingTableCache();
+
+ Assert.assertEquals(concurrentMap,latestCache);
+
+ Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+
+
+ Assert.assertEquals(servicesGlobal.size(),1);
+
+ Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+
+ rti.removeGlobalRoute(routeIdentifier);
+
+ Assert.assertNotNull(rti.getRoutes(routeIdentifier));
+
+
+ }
+
+ private ConcurrentMap createRoutingTableCache() throws Exception {
+
+ //here init
+ Component c = mock(Component.class);
+
+ when(ics.existCache(
+ RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(false);
+
+ when(ics.createCache(RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(concurrentMapMock);
+ rti.setClusterGlobalServices(this.ics);
+ rti.init(c);
+
+ Assert.assertEquals(concurrentMapMock,rti.getRoutingTableCache() );
+ return concurrentMapMock;
+
+ }
+
+
+ @Test
+ public void testCreateRoutingTableCacheReturnExistingCache() throws Exception {
+ ConcurrentMap concurrentMap = createRoutingTableCache();
+
+ //OK here we should try creating again the cache but this time it should return the existing one
+ when(ics.existCache(
+ RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(true);
+
+ when(ics.getCache(
+ RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(concurrentMap);
+
+
+ //here init
+ Component c = mock(Component.class);
+
+ rti.init(c);
+
+ Assert.assertEquals(concurrentMap,rti.getRoutingTableCache());
+
+
+
+
+
+ }
+
+ private class RouteChangeListenerImpl<I,R> implements RouteChangeListener<I,R>{
+
+ @Override
+ public void onRouteUpdated(I key, R new_value) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void onRouteDeleted(I key) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ }
+
+}
--- /dev/null
+<?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>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.integrationtest</artifactId>
+ <version>0.5.1-SNAPSHOT</version>
+ <relativePath>../../../commons/integrationtest</relativePath>
+ </parent>
+ <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>
+ <tag>HEAD</tag>
+ </scm>
+
+ <artifactId>zeromq-routingtable.integrationtest</artifactId>
+ <version>0.4.1-SNAPSHOT</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>zeromq-routingtable.implementation</artifactId>
+ <version>0.4.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal.implementation</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.it.implementation</artifactId>
+ <version>0.5.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.stub</artifactId>
+ <version>0.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.url</groupId>
+ <artifactId>pax-url-aether</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>eclipselink</groupId>
+ <artifactId>javax.resource</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal</artifactId>
+ <version>0.5.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-connector-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-manager</artifactId>
+ <version>0.2.3-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ <version>3.8.1.v20120830-144521</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>log4j-over-slf4j</artifactId>
+ <version>1.7.2</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>1.0.9</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.0.9</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>antlr4-runtime-osgi-nohead</artifactId>
+ <version>4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>xtend-lib-osgi</artifactId>
+ <version>2.4.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>clustering.services</artifactId>
+ <version>0.4.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ <version>3.8.1.v20120830-144521</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>3.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>3.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam</artifactId>
+ <version>3.0.0</version>
+ </dependency>
+ </dependencies>
+ <properties>
+ <!-- Sonar jacoco plugin to get integration test coverage info -->
+ <sonar.jacoco.reportPath>../implementation/target/jacoco.exec</sonar.jacoco.reportPath>
+ <sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
+ </properties>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>0.5.3.201107060350</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>${jacoco.version}</version>
+ <configuration>
+ <destFile>../implementation/target/jacoco-it.exec</destFile>
+ <includes>org.opendaylight.controller.*</includes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+package org.opendaylight.controller.sal.connector.remoterpc.impl;
+
+import junit.framework.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
+import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.util.PathUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Set;
+
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemPackages;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+
+
+
+@RunWith(PaxExam.class)
+public class
+ ZeroMQRoutingTableTestIT {
+ private Logger log = LoggerFactory
+ .getLogger(ZeroMQRoutingTableTestIT.class);
+
+ public static final String ODL = "org.opendaylight.controller";
+ public static final String YANG = "org.opendaylight.yangtools";
+ public static final String CONTROLLER = "org.opendaylight.controller";
+ public static final String YANGTOOLS = "org.opendaylight.yangtools";
+ RoutingIdentifierImpl rii = new RoutingIdentifierImpl();
+ // get the OSGI bundle context
+ @Inject
+ private BundleContext bc;
+ @Inject
+ private RoutingTable routingTable = null;
+
+ // Configure the OSGi container
+ @Configuration
+ public Option[] config() {
+ return options(
+ //
+ systemProperty("logback.configurationFile").value(
+ "file:" + PathUtils.getBaseDir()
+ + "/src/test/resources/logback.xml"),
+ // To start OSGi console for inspection remotely
+ systemProperty("osgi.console").value("2401"),
+ // Set the systemPackages (used by clustering)
+ systemPackages("sun.reflect", "sun.reflect.misc", "sun.misc"),
+ // List framework bundles
+
+ mavenBundle("equinoxSDK381",
+ "org.eclipse.equinox.console").versionAsInProject(),
+ mavenBundle("equinoxSDK381",
+ "org.eclipse.equinox.util").versionAsInProject(),
+ mavenBundle("equinoxSDK381",
+ "org.eclipse.osgi.services").versionAsInProject(),
+ mavenBundle("equinoxSDK381",
+ "org.eclipse.equinox.ds").versionAsInProject(),
+ mavenBundle("equinoxSDK381",
+ "org.apache.felix.gogo.command").versionAsInProject(),
+ mavenBundle("equinoxSDK381",
+ "org.apache.felix.gogo.runtime").versionAsInProject(),
+ mavenBundle("equinoxSDK381",
+ "org.apache.felix.gogo.shell").versionAsInProject(),
+ // List logger bundles
+ mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(),
+ mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(),
+ mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(),
+ mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(),
+ // List all the bundles on which the test case depends
+ mavenBundle(ODL,
+ "clustering.services").versionAsInProject(),
+
+ mavenBundle(ODL, "sal").versionAsInProject(),
+ mavenBundle(ODL,
+ "sal.implementation").versionAsInProject(),
+ mavenBundle(ODL, "containermanager").versionAsInProject(),
+ mavenBundle(ODL,
+ "containermanager.it.implementation").versionAsInProject(),
+ mavenBundle("org.jboss.spec.javax.transaction",
+ "jboss-transaction-api_1.1_spec").versionAsInProject(),
+ mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),
+ mavenBundle("org.apache.felix",
+ "org.apache.felix.dependencymanager").versionAsInProject(),
+ mavenBundle("org.apache.felix",
+ "org.apache.felix.dependencymanager.shell").versionAsInProject(),
+ mavenBundle("eclipselink", "javax.resource").versionAsInProject(),
+
+ mavenBundle("com.google.guava","guava").versionAsInProject(),
+ // List logger bundles
+ mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(),
+ mavenBundle("org.slf4j", "log4j-over-slf4j")
+ .versionAsInProject(),
+ mavenBundle("ch.qos.logback", "logback-core")
+ .versionAsInProject(),
+ mavenBundle("ch.qos.logback", "logback-classic")
+ .versionAsInProject(),
+
+ mavenBundle(ODL, "clustering.services")
+ .versionAsInProject(),
+ mavenBundle(ODL, "clustering.stub")
+ .versionAsInProject(),
+
+
+ // List all the bundles on which the test case depends
+ mavenBundle(ODL, "sal")
+ .versionAsInProject(),
+ mavenBundle(ODL, "sal-connector-api")
+ .versionAsInProject(),
+ mavenBundle(ODL, "zeromq-routingtable.implementation")
+ .versionAsInProject(),
+
+ mavenBundle("org.jboss.spec.javax.transaction",
+ "jboss-transaction-api_1.1_spec").versionAsInProject(),
+ mavenBundle("org.apache.commons", "commons-lang3")
+ .versionAsInProject(),
+ mavenBundle("org.apache.felix",
+ "org.apache.felix.dependencymanager")
+ .versionAsInProject(),
+
+ mavenBundle(ODL,
+ "sal-core-api")
+ .versionAsInProject(),
+ mavenBundle("org.opendaylight.yangtools","yang-data-api")
+ .versionAsInProject(),
+ mavenBundle("org.opendaylight.yangtools","yang-model-api")
+ .versionAsInProject(),
+ mavenBundle("org.opendaylight.yangtools","yang-binding")
+ .versionAsInProject(),
+
+ mavenBundle(CONTROLLER, "sal-binding-api").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "sal-binding-config").versionAsInProject(),
+ mavenBundle(CONTROLLER, "sal-binding-broker-impl").versionAsInProject(), //
+ mavenBundle("org.javassist", "javassist").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "sal-common-util").versionAsInProject(), //
+
+ mavenBundle(YANGTOOLS, "yang-data-api").versionAsInProject(), //
+ mavenBundle(YANGTOOLS, "yang-data-impl").versionAsInProject(), //
+ mavenBundle(YANGTOOLS, "yang-model-api").versionAsInProject(), //
+ mavenBundle(YANGTOOLS, "yang-model-util").versionAsInProject(), //
+ mavenBundle(YANGTOOLS, "yang-parser-api").versionAsInProject(),
+ mavenBundle(YANGTOOLS, "yang-parser-impl").versionAsInProject(),
+
+
+ mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(), //
+ mavenBundle(YANGTOOLS, "binding-model-api").versionAsInProject(), //
+ mavenBundle(YANGTOOLS, "binding-generator-util").versionAsInProject(),
+ mavenBundle(YANGTOOLS, "yang-parser-impl").versionAsInProject(),
+ mavenBundle(YANGTOOLS, "binding-type-provider").versionAsInProject(),
+ mavenBundle(YANGTOOLS, "binding-generator-api").versionAsInProject(),
+ mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(),
+ mavenBundle(YANGTOOLS, "binding-generator-impl").versionAsInProject(),
+
+
+ mavenBundle(CONTROLLER, "sal-core-api").versionAsInProject().update(), //
+ mavenBundle(CONTROLLER, "sal-broker-impl").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "sal-core-spi").versionAsInProject().update(), //
+
+ mavenBundle(YANGTOOLS + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), //
+
+ mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(),
+ mavenBundle("com.google.guava", "guava").versionAsInProject(), //
+ mavenBundle("org.javassist", "javassist").versionAsInProject(),
+ mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), //
+ mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
+ mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
+ mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
+
+ mavenBundle(ODL, "sal-common").versionAsInProject(), //
+ mavenBundle(ODL, "sal-common-api").versionAsInProject(),//
+ mavenBundle(ODL, "sal-common-impl").versionAsInProject(), //
+ mavenBundle(ODL, "sal-common-util").versionAsInProject(), //
+
+ mavenBundle(ODL, "config-api").versionAsInProject(), //
+ mavenBundle(ODL, "config-manager").versionAsInProject(), //
+ mavenBundle("commons-io", "commons-io").versionAsInProject(),
+ mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),
+
+ mavenBundle(ODL, "sal-binding-api").versionAsInProject(), //
+ mavenBundle(ODL, "sal-binding-config").versionAsInProject(),
+ mavenBundle("org.javassist", "javassist").versionAsInProject(), //
+ mavenBundle(ODL, "sal-common-util").versionAsInProject(), //
+
+ mavenBundle(YANG, "yang-data-api").versionAsInProject(), //
+ mavenBundle(YANG, "yang-data-impl").versionAsInProject(), //
+ mavenBundle(YANG, "yang-model-api").versionAsInProject(), //
+ mavenBundle(YANG, "yang-model-util").versionAsInProject(), //
+ mavenBundle(YANG, "yang-parser-api").versionAsInProject(),
+ mavenBundle(YANG, "yang-parser-impl").versionAsInProject(),
+
+
+ mavenBundle(YANG, "binding-generator-spi").versionAsInProject(), //
+ mavenBundle(YANG, "binding-model-api").versionAsInProject(), //
+ mavenBundle(YANG, "binding-generator-util").versionAsInProject(),
+ mavenBundle(YANG, "yang-parser-impl").versionAsInProject(),
+ mavenBundle(YANG, "binding-type-provider").versionAsInProject(),
+ mavenBundle(YANG, "binding-generator-api").versionAsInProject(),
+ mavenBundle(YANG, "binding-generator-spi").versionAsInProject(),
+ mavenBundle(YANG, "binding-generator-impl").versionAsInProject(),
+
+
+ mavenBundle(ODL, "sal-core-api").versionAsInProject().update(), //
+ mavenBundle(ODL, "sal-broker-impl").versionAsInProject(), //
+ mavenBundle(ODL, "sal-core-spi").versionAsInProject().update(), //
+
+ mavenBundle(YANG + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), //
+
+ mavenBundle(YANG, "concepts").versionAsInProject(),
+ mavenBundle(YANG, "yang-binding").versionAsInProject(), //
+ mavenBundle(YANG, "yang-common").versionAsInProject(), //
+ mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(),
+ mavenBundle("com.google.guava", "guava").versionAsInProject(), //
+ mavenBundle("org.javassist", "javassist").versionAsInProject(),
+
+ junitBundles());
+ }
+
+ private String stateToString(int state) {
+ switch (state) {
+ case Bundle.ACTIVE:
+ return "ACTIVE";
+ case Bundle.INSTALLED:
+ return "INSTALLED";
+ case Bundle.RESOLVED:
+ return "RESOLVED";
+ case Bundle.UNINSTALLED:
+ return "UNINSTALLED";
+ default:
+ return "Not CONVERTED";
+ }
+ }
+
+ @Test
+ public void testAddGlobalRoute () throws Exception{
+
+ routingTable.addGlobalRoute(rii,"172.27.12.1:5000");
+
+ Set<String> routes = routingTable.getRoutes(rii);
+
+ for(String route:routes){
+ Assert.assertEquals(route,"172.27.12.1:5000");
+ }
+
+
+ }
+
+
+ @Test
+ public void testDeleteGlobalRoute () throws Exception{
+
+ routingTable.removeGlobalRoute(rii);
+
+ Set<String> routes = routingTable.getRoutes(rii);
+
+ Assert.assertNull(routes);
+
+
+ }
+
+
+
+ class RoutingIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable {
+
+ private final URI namespace = URI.create("http://cisco.com/example");
+ private final QName QNAME = new QName(namespace,"global");
+ private final QName instance = new QName(URI.create("127.0.0.1"),"local");
+
+ @Override
+ public QName getContext() {
+ return QNAME;
+ }
+
+ @Override
+ public QName getType() {
+ return QNAME;
+ }
+
+ @Override
+ public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getRoute() {
+ return InstanceIdentifier.of(instance);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RoutingIdentifierImpl that = (RoutingIdentifierImpl) o;
+
+ if (QNAME != null ? !QNAME.equals(that.QNAME) : that.QNAME != null) return false;
+ if (instance != null ? !instance.equals(that.instance) : that.instance != null) return false;
+ if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = namespace != null ? namespace.hashCode() : 0;
+ result = 31 * result + (QNAME != null ? QNAME.hashCode() : 0);
+ result = 31 * result + (instance != null ? instance.hashCode() : 0);
+ return result;
+ }
+ }
+
+
+
+
+
+}
--- /dev/null
+<configuration scan="true">
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+ <root level="error">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration>
private static Set<Capability> setupCapabilities(YangStoreSnapshot yangStoreSnapshot) {
Set<Capability> capabilities = Sets.newHashSet();
+ // [RFC6241] 8.3. Candidate Configuration Capability
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+ // [RFC6241] 8.5. Rollback-on-Error Capability
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
- capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.0"));
- capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.1"));
- capabilities
- .add(new BasicCapability(
- "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04"));
+ // [RFC6022] get-schema RPC. TODO: implement rest of the RFC
+ capabilities.add(new BasicCapability("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04"));
final Collection<Map.Entry<Module, String>> modulesAndContents = yangStoreSnapshot.getModuleMap().values();
for (Map.Entry<Module, String> moduleAndContent : modulesAndContents) {
@Override
public synchronized void close() {
for (ObjectName tx : allOpenedTransactions) {
- if (isStillOpenTransaction(tx)) {
- try {
+ try {
+ if (isStillOpenTransaction(tx)) {
configRegistryClient.getConfigTransactionClient(tx).abortConfig();
- } catch (Exception e) {
- logger.debug("Ignoring {} while closing transaction {}", e.toString(), tx, e);
}
+ } catch (Exception e) {
+ logger.debug("Ignoring exception while closing transaction {}", tx, e);
}
}
allOpenedTransactions.clear();
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
private final InetSocketAddress address;
- private final NetconfClientDispatcher dispatcher;
private final EventLoopGroup nettyThreadgroup;
+ private NetconfClientDispatcher netconfClientDispatcher;
private NetconfClient netconfClient;
private final Persister persister;
this.timeout = timeout;
this.nettyThreadgroup = new NioEventLoopGroup();
- this.dispatcher = new NetconfClientDispatcher(Optional.<SSLContext>absent(), nettyThreadgroup, nettyThreadgroup);
}
public void init() throws InterruptedException {
while (true) {
attempt++;
+ netconfClientDispatcher = new NetconfClientDispatcher(Optional.<SSLContext>absent(), nettyThreadgroup, nettyThreadgroup);
try {
- netconfClient = new NetconfClient(this.toString(), address, delay, dispatcher);
- // TODO is this correct ex to catch ?
+ netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher);
} catch (IllegalStateException e) {
logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
+ netconfClientDispatcher.close();
Thread.sleep(delay);
continue;
}
logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities);
- try {
- netconfClient.close();
- } catch (IOException e) {
- throw new RuntimeException("Error closing temporary client " + netconfClient);
- }
+ closeClientAndDispatcher(netconfClient, netconfClientDispatcher);
Thread.sleep(delay);
}
}
+ private static void closeClientAndDispatcher(Closeable client, Closeable dispatcher) {
+ Exception fromClient = null;
+ try {
+ client.close();
+ } catch (Exception e) {
+ fromClient = e;
+ } finally {
+ try {
+ dispatcher.close();
+ } catch (Exception e) {
+ if (fromClient != null) {
+ e.addSuppressed(fromClient);
+ }
+
+ throw new RuntimeException("Error closing temporary client ", e);
+ }
+ }
+ }
+
private boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
for (String exCap : expectedCaps) {
if (currentCapabilities.contains(exCap) == false)
}
}
+ if (netconfClientDispatcher != null) {
+ try {
+ netconfClientDispatcher.close();
+ } catch (Exception e) {
+ logger.warn("Unable to close connection to netconf {}", netconfClientDispatcher, e);
+ }
+ }
+
try {
nettyThreadgroup.shutdownGracefully();
} catch (Exception e) {
- logger.warn("Unable to close netconf client thread group {}", dispatcher, e);
+ logger.warn("Unable to close netconf client thread group {}", netconfClientDispatcher, e);
}
// unregister from JMX
import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.protocol.framework.SessionListener;
import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
+import java.io.Closeable;
import java.net.InetSocketAddress;
-public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> {
+public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> implements Closeable {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfClient.class);
private final Optional<SSLContext> maybeContext;
private final NetconfClientSessionNegotiatorFactory negotatorFactory;
+ private final HashedWheelTimer timer;
public NetconfClientDispatcher(final Optional<SSLContext> maybeContext, EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
super(bossGroup, workerGroup);
this.maybeContext = Preconditions.checkNotNull(maybeContext);
- this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer());
+ timer = new HashedWheelTimer();
+ this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer);
}
public Future<NetconfClientSession> createClient(InetSocketAddress address,
}
}
+ @Override
+ public void close() {
+ try {
+ timer.stop();
+ } catch (Exception e) {
+ logger.debug("Ignoring exception while closing {}", timer, e);
+ }
+ }
}
private DefaultCommitNotificationProducer commitNot;
private NetconfServerDispatcher dispatch;
private NioEventLoopGroup eventLoopGroup;
+ private HashedWheelTimer timer;
@Override
public void start(final BundleContext context) throws Exception {
factoriesTracker.open();
SessionIdProvider idProvider = new SessionIdProvider();
+ timer = new HashedWheelTimer();
NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
- new HashedWheelTimer(), factoriesListener, idProvider);
+ timer, factoriesListener, idProvider);
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
commitNot.close();
eventLoopGroup.shutdownGracefully();
+ timer.stop();
}
}
public void tearDown() throws Exception {
commitNot.close();
nettyThreadgroup.shutdownGracefully();
+ clientDispatcher.close();
}
private void loadMessages() throws IOException, SAXException, ParserConfigurationException {
import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
import org.opendaylight.controller.sal.utils.GlobalConstants;
import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
import org.opendaylight.controller.sal.utils.ObjectReader;
import org.opendaylight.controller.sal.utils.ObjectWriter;
import org.opendaylight.controller.sal.utils.Status;
|| e.getTailNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION));
}
+ /**
+ * This method cross checks the determination of nodeConnector type by Discovery Service
+ * against the information in SwitchManager and updates it accordingly.
+ * @param e
+ * The edge
+ */
+ private void crossCheckNodeConnectors(Edge e) {
+ NodeConnector nc;
+ if (e.getHeadNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+ nc = updateNCTypeFromSwitchMgr(e.getHeadNodeConnector());
+ if (nc != null) {
+ e.setHeadNodeConnector(nc);
+ }
+ }
+ if (e.getTailNodeConnector().getType().equals(NodeConnector.NodeConnectorIDType.PRODUCTION)) {
+ nc = updateNCTypeFromSwitchMgr(e.getTailNodeConnector());
+ if (nc != null) {
+ e.setTailNodeConnector(nc);
+ }
+ }
+ }
+
+ /**
+ * A NodeConnector may have been categorized as of type Production by Discovery Service.
+ * But at the time when this determination was made, only OF nodes were known to Discovery
+ * Service. This method checks if the node of nodeConnector is known to SwitchManager. If
+ * so, then it returns a new NodeConnector with correct type.
+ *
+ * @param nc
+ * NodeConnector as passed on in the edge
+ * @return
+ * If Node of the NodeConnector is in SwitchManager, then return a new NodeConnector
+ * with correct type, null otherwise
+ */
+
+ private NodeConnector updateNCTypeFromSwitchMgr(NodeConnector nc) {
+
+ for (Node node : switchManager.getNodes()) {
+ String nodeName = node.getNodeIDString();
+ log.trace("Switch Manager Node Name: {}, NodeConnector Node Name: {}", nodeName,
+ nc.getNode().getNodeIDString());
+ if (nodeName.equals(nc.getNode().getNodeIDString())) {
+ NodeConnector nodeConnector = NodeConnectorCreator
+ .createNodeConnector(node.getType(), nc.getID(), node);
+ return nodeConnector;
+ }
+ }
+ return null;
+ }
+
/**
* The Map returned is a copy of the current topology hence if the topology
* changes the copy doesn't
return null;
}
+ // Check if nodeConnectors of the edge were correctly categorized
+ // by OF plugin
+ crossCheckNodeConnectors(e);
+
// Make sure the props are non-null
if (props == null) {
props = new HashSet<Property>();
--- /dev/null
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Vector;
+
+import ch.ethz.ssh2.ChannelCondition;
+import ch.ethz.ssh2.PtySettings;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.ServerSessionCallback;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.PacketChannelFailure;
+import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
+import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
+import ch.ethz.ssh2.packets.PacketChannelSuccess;
+import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
+import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
+import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
+import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
+import ch.ethz.ssh2.packets.PacketSessionExecCommand;
+import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
+import ch.ethz.ssh2.packets.PacketSessionStartShell;
+import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
+import ch.ethz.ssh2.packets.PacketSessionX11Request;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.transport.MessageHandler;
+import ch.ethz.ssh2.transport.TransportManager;
+
+/**
+ * ChannelManager. Please read the comments in Channel.java.
+ * <p/>
+ * Besides the crypto part, this is the core of the library.
+ *
+ * @author Christian Plattner
+ * @version $Id: ChannelManager.java 48 2013-08-01 12:22:33Z cleondris@gmail.com $
+ */
+public class ChannelManager implements MessageHandler
+{
+ private static final Logger log = Logger.getLogger(ChannelManager.class);
+
+ private final ServerConnectionState server_state;
+ private final TransportManager tm;
+
+ private final HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
+
+ private final List<Channel> channels = new Vector<Channel>();
+ private int nextLocalChannel = 100;
+ private boolean shutdown = false;
+ private int globalSuccessCounter = 0;
+ private int globalFailedCounter = 0;
+
+ private final HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
+
+ private final List<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
+
+ private boolean listenerThreadsAllowed = true;
+
+ /**
+ * Constructor for client-mode.
+ * @param tm
+ */
+ public ChannelManager(TransportManager tm)
+ {
+ this.server_state = null;
+ this.tm = tm;
+ tm.registerMessageHandler(this, 80, 100);
+ }
+
+ /**
+ * Constructor for server-mode.
+ * @param state
+ */
+ public ChannelManager(ServerConnectionState state)
+ {
+ this.server_state = state;
+ this.tm = state.tm;
+ tm.registerMessageHandler(this, 80, 100);
+ }
+
+ private Channel getChannel(int id)
+ {
+ synchronized (channels)
+ {
+ for (Channel c : channels)
+ {
+ if (c.localID == id)
+ return c;
+ }
+ }
+ return null;
+ }
+
+ private void removeChannel(int id)
+ {
+ synchronized (channels)
+ {
+ for (Channel c : channels)
+ {
+ if (c.localID == id)
+ {
+ channels.remove(c);
+ break;
+ }
+ }
+ }
+ }
+
+ private int addChannel(Channel c)
+ {
+ synchronized (channels)
+ {
+ channels.add(c);
+ return nextLocalChannel++;
+ }
+ }
+
+ private void waitUntilChannelOpen(Channel c) throws IOException
+ {
+ boolean wasInterrupted = false;
+
+ synchronized (c)
+ {
+ while (c.state == Channel.STATE_OPENING)
+ {
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ wasInterrupted = true;
+ }
+ }
+
+ if (c.state != Channel.STATE_OPEN)
+ {
+ removeChannel(c.localID);
+
+ String detail = c.getReasonClosed();
+
+ if (detail == null)
+ detail = "state: " + c.state;
+
+ throw new IOException("Could not open channel (" + detail + ")");
+ }
+ }
+
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+
+ private void waitForGlobalSuccessOrFailure() throws IOException
+ {
+ boolean wasInterrupted = false;
+
+ try
+ {
+ synchronized (channels)
+ {
+ while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
+ {
+ if (shutdown)
+ {
+ throw new IOException("The connection is being shutdown");
+ }
+
+ try
+ {
+ channels.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ wasInterrupted = true;
+ }
+ }
+
+ if (globalFailedCounter != 0)
+ {
+ throw new IOException("The server denied the request (did you enable port forwarding?)");
+ }
+
+ if (globalSuccessCounter == 0)
+ {
+ throw new IOException("Illegal state.");
+ }
+ }
+ }
+ finally
+ {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private void waitForChannelSuccessOrFailure(Channel c) throws IOException
+ {
+ boolean wasInterrupted = false;
+
+ try
+ {
+ synchronized (c)
+ {
+ while ((c.successCounter == 0) && (c.failedCounter == 0))
+ {
+ if (c.state != Channel.STATE_OPEN)
+ {
+ String detail = c.getReasonClosed();
+
+ if (detail == null)
+ detail = "state: " + c.state;
+
+ throw new IOException("This SSH2 channel is not open (" + detail + ")");
+ }
+
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ wasInterrupted = true;
+ }
+ }
+
+ if (c.failedCounter != 0)
+ {
+ throw new IOException("The server denied the request.");
+ }
+ }
+ }
+ finally
+ {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
+ {
+ synchronized (x11_magic_cookies)
+ {
+ x11_magic_cookies.put(hexFakeCookie, data);
+ }
+ }
+
+ public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
+ {
+ if (hexFakeCookie == null)
+ throw new IllegalStateException("hexFakeCookie may not be null");
+
+ synchronized (x11_magic_cookies)
+ {
+ x11_magic_cookies.remove(hexFakeCookie);
+ }
+
+ if (killChannels == false)
+ return;
+
+ log.debug("Closing all X11 channels for the given fake cookie");
+
+ List<Channel> channel_copy = new Vector<Channel>();
+
+ synchronized (channels)
+ {
+ channel_copy.addAll(channels);
+ }
+
+ for (Channel c : channel_copy)
+ {
+ synchronized (c)
+ {
+ if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
+ continue;
+ }
+
+ try
+ {
+ closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+
+ public X11ServerData checkX11Cookie(String hexFakeCookie)
+ {
+ synchronized (x11_magic_cookies)
+ {
+ if (hexFakeCookie != null)
+ return x11_magic_cookies.get(hexFakeCookie);
+ }
+ return null;
+ }
+
+ public void closeAllChannels()
+ {
+ log.debug("Closing all channels");
+
+ List<Channel> channel_copy = new Vector<Channel>();
+
+ synchronized (channels)
+ {
+ channel_copy.addAll(channels);
+ }
+
+ for (Channel c : channel_copy)
+ {
+ try
+ {
+ closeChannel(c, "Closing all channels", true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+
+ public void closeChannel(Channel c, String reason, boolean force) throws IOException
+ {
+ byte msg[] = new byte[5];
+
+ synchronized (c)
+ {
+ if (force)
+ {
+ c.state = Channel.STATE_CLOSED;
+ c.EOF = true;
+ }
+
+ c.setReasonClosed(reason);
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+
+ c.notifyAll();
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ return;
+ tm.sendMessage(msg);
+ c.closeMessageSent = true;
+ }
+
+ log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
+ }
+
+ public void sendEOF(Channel c) throws IOException
+ {
+ byte[] msg = new byte[5];
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ return;
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ return;
+ tm.sendMessage(msg);
+ }
+
+
+ log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
+ }
+
+ public void sendOpenConfirmation(Channel c) throws IOException
+ {
+ PacketChannelOpenConfirmation pcoc = null;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPENING)
+ return;
+
+ c.state = Channel.STATE_OPEN;
+
+ pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ return;
+ tm.sendMessage(pcoc.getPayload());
+ }
+ }
+
+ public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
+ {
+ boolean wasInterrupted = false;
+
+ try
+ {
+ while (len > 0)
+ {
+ int thislen = 0;
+ byte[] msg;
+
+ synchronized (c)
+ {
+ while (true)
+ {
+ if (c.state == Channel.STATE_CLOSED)
+ throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
+
+ if (c.state != Channel.STATE_OPEN)
+ throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
+
+ if (c.remoteWindow != 0)
+ break;
+
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ wasInterrupted = true;
+ }
+ }
+
+ /* len > 0, no sign extension can happen when comparing */
+
+ thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
+
+ int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
+
+ /* The worst case scenario =) a true bottleneck */
+
+ if (estimatedMaxDataLen <= 0)
+ {
+ estimatedMaxDataLen = 1;
+ }
+
+ if (thislen > estimatedMaxDataLen)
+ thislen = estimatedMaxDataLen;
+
+ c.remoteWindow -= thislen;
+
+ msg = new byte[1 + 8 + thislen];
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+ msg[5] = (byte) (thislen >> 24);
+ msg[6] = (byte) (thislen >> 16);
+ msg[7] = (byte) (thislen >> 8);
+ msg[8] = (byte) (thislen);
+
+ System.arraycopy(buffer, pos, msg, 9, thislen);
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent == true)
+ throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
+
+ tm.sendMessage(msg);
+ }
+
+ pos += thislen;
+ len -= thislen;
+ }
+ }
+ finally
+ {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
+ throws IOException
+ {
+ RemoteForwardingData rfd = new RemoteForwardingData();
+
+ rfd.bindAddress = bindAddress;
+ rfd.bindPort = bindPort;
+ rfd.targetAddress = targetAddress;
+ rfd.targetPort = targetPort;
+
+ synchronized (remoteForwardings)
+ {
+ Integer key = new Integer(bindPort);
+
+ if (remoteForwardings.get(key) != null)
+ {
+ throw new IOException("There is already a forwarding for remote port " + bindPort);
+ }
+
+ remoteForwardings.put(key, rfd);
+ }
+
+ synchronized (channels)
+ {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
+ tm.sendMessage(pgf.getPayload());
+
+ log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
+
+ try
+ {
+ waitForGlobalSuccessOrFailure();
+ }
+ catch (IOException e)
+ {
+ synchronized (remoteForwardings)
+ {
+ remoteForwardings.remove(rfd);
+ }
+ throw e;
+ }
+
+ return bindPort;
+ }
+
+ public void requestCancelGlobalForward(int bindPort) throws IOException
+ {
+ RemoteForwardingData rfd = null;
+
+ synchronized (remoteForwardings)
+ {
+ rfd = remoteForwardings.get(new Integer(bindPort));
+
+ if (rfd == null)
+ throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
+ }
+
+ synchronized (channels)
+ {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
+ rfd.bindPort);
+ tm.sendMessage(pgcf.getPayload());
+
+ log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
+
+ waitForGlobalSuccessOrFailure();
+
+ /* Only now we are sure that no more forwarded connections will arrive */
+
+ synchronized (remoteForwardings)
+ {
+ remoteForwardings.remove(rfd);
+ }
+ }
+
+ public void registerThread(IChannelWorkerThread thr) throws IOException
+ {
+ synchronized (listenerThreads)
+ {
+ if (listenerThreadsAllowed == false)
+ throw new IOException("Too late, this connection is closed.");
+ listenerThreads.add(thr);
+ }
+ }
+
+ public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
+ int originator_port) throws IOException
+ {
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.localID = addChannel(c);
+ // end of synchronized block forces writing out to main memory
+ }
+
+ PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
+ c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
+
+ tm.sendMessage(dtc.getPayload());
+
+ waitUntilChannelOpen(c);
+
+ return c;
+ }
+
+ public Channel openSessionChannel() throws IOException
+ {
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.localID = addChannel(c);
+ // end of synchronized block forces the writing out to main memory
+ }
+
+ log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
+
+ PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
+ tm.sendMessage(smo.getPayload());
+
+ waitUntilChannelOpen(c);
+
+ return c;
+ }
+
+ public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
+ int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
+ {
+ PacketSessionPtyRequest spr;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
+
+ spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
+ term_width_pixels, term_height_pixels, terminal_modes);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(spr.getPayload());
+ }
+
+ try
+ {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("PTY request failed").initCause(e);
+ }
+ }
+
+ public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
+ String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
+ {
+ PacketSessionX11Request psr;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
+
+ psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
+ x11AuthenticationCookie, x11ScreenNumber);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(psr.getPayload());
+ }
+
+ log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
+
+ try
+ {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The X11 request failed.").initCause(e);
+ }
+ }
+
+ public void requestSubSystem(Channel c, String subSystemName) throws IOException
+ {
+ PacketSessionSubsystemRequest ssr;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
+
+ ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(ssr.getPayload());
+ }
+
+ try
+ {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The subsystem request failed.").initCause(e);
+ }
+ }
+
+ public void requestExecCommand(Channel c, String cmd) throws IOException
+ {
+ this.requestExecCommand(c, cmd, null);
+ }
+
+ /**
+ * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
+ */
+ public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException
+ {
+ PacketSessionExecCommand sm;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
+
+ sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(sm.getPayload(charsetName));
+ }
+
+ log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
+
+ try
+ {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The execute request failed.").initCause(e);
+ }
+ }
+
+ public void requestShell(Channel c) throws IOException
+ {
+ PacketSessionStartShell sm;
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
+
+ sm = new PacketSessionStartShell(c.remoteID, true);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized (c.channelSendLock)
+ {
+ if (c.closeMessageSent)
+ throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
+ tm.sendMessage(sm.getPayload());
+ }
+
+ try
+ {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch (IOException e)
+ {
+ throw (IOException) new IOException("The shell request failed.").initCause(e);
+ }
+ }
+
+ public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen <= 13)
+ throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+ int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
+
+ if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
+ throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
+
+ if (len != (msglen - 13))
+ throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
+ + ", got " + len + ")");
+
+ log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
+
+ synchronized (c)
+ {
+ if (c.state == Channel.STATE_CLOSED)
+ return; // ignore
+
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
+ + c.state + ")");
+
+ if (c.localWindow < len)
+ throw new IOException("Remote sent too much data, does not fit into window.");
+
+ c.localWindow -= len;
+
+ System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
+ c.stderrWritepos += len;
+
+ c.notifyAll();
+ }
+ }
+
+ /**
+ * Wait until for a condition.
+ *
+ * @param c Channel
+ * @param timeout in ms, 0 means no timeout.
+ * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
+ * @return all current events
+ */
+ public int waitForCondition(Channel c, long timeout, int condition_mask)
+ {
+ boolean wasInterrupted = false;
+
+ try
+ {
+ long end_time = 0;
+ boolean end_time_set = false;
+
+ synchronized (c)
+ {
+ while (true)
+ {
+ int current_cond = 0;
+
+ int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
+ int stderrAvail = c.stderrWritepos - c.stderrReadpos;
+
+ if (stdoutAvail > 0)
+ current_cond = current_cond | ChannelCondition.STDOUT_DATA;
+
+ if (stderrAvail > 0)
+ current_cond = current_cond | ChannelCondition.STDERR_DATA;
+
+ if (c.EOF)
+ current_cond = current_cond | ChannelCondition.EOF;
+
+ if (c.getExitStatus() != null)
+ current_cond = current_cond | ChannelCondition.EXIT_STATUS;
+
+ if (c.getExitSignal() != null)
+ current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
+
+ if (c.state == Channel.STATE_CLOSED)
+ return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
+
+ if ((current_cond & condition_mask) != 0)
+ return current_cond;
+
+ if (timeout > 0)
+ {
+ if (!end_time_set)
+ {
+ end_time = System.currentTimeMillis() + timeout;
+ end_time_set = true;
+ }
+ else
+ {
+ timeout = end_time - System.currentTimeMillis();
+
+ if (timeout <= 0)
+ return current_cond | ChannelCondition.TIMEOUT;
+ }
+ }
+
+ try
+ {
+ if (timeout > 0)
+ c.wait(timeout);
+ else
+ c.wait();
+ }
+ catch (InterruptedException e)
+ {
+ wasInterrupted = true;
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public int getAvailable(Channel c, boolean extended) throws IOException
+ {
+ synchronized (c)
+ {
+ int avail;
+
+ if (extended)
+ avail = c.stderrWritepos - c.stderrReadpos;
+ else
+ avail = c.stdoutWritepos - c.stdoutReadpos;
+
+ return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
+ }
+ }
+
+ public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
+ {
+ boolean wasInterrupted = false;
+
+ try
+ {
+ int copylen = 0;
+ int increment = 0;
+ int remoteID = 0;
+ int localID = 0;
+
+ synchronized (c)
+ {
+ int stdoutAvail = 0;
+ int stderrAvail = 0;
+
+ while (true)
+ {
+ /*
+ * Data available? We have to return remaining data even if the
+ * channel is already closed.
+ */
+
+ stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
+ stderrAvail = c.stderrWritepos - c.stderrReadpos;
+
+ if ((!extended) && (stdoutAvail != 0))
+ break;
+
+ if ((extended) && (stderrAvail != 0))
+ break;
+
+ /* Do not wait if more data will never arrive (EOF or CLOSED) */
+
+ if ((c.EOF) || (c.state != Channel.STATE_OPEN))
+ return -1;
+
+ try
+ {
+ c.wait();
+ }
+ catch (InterruptedException ignore)
+ {
+ wasInterrupted = true;
+ }
+ }
+
+ /* OK, there is some data. Return it. */
+
+ if (!extended)
+ {
+ copylen = (stdoutAvail > len) ? len : stdoutAvail;
+ System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
+ c.stdoutReadpos += copylen;
+
+ if (c.stdoutReadpos != c.stdoutWritepos)
+
+ System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
+ - c.stdoutReadpos);
+
+ c.stdoutWritepos -= c.stdoutReadpos;
+ c.stdoutReadpos = 0;
+ }
+ else
+ {
+ copylen = (stderrAvail > len) ? len : stderrAvail;
+ System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
+ c.stderrReadpos += copylen;
+
+ if (c.stderrReadpos != c.stderrWritepos)
+
+ System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
+ - c.stderrReadpos);
+
+ c.stderrWritepos -= c.stderrReadpos;
+ c.stderrReadpos = 0;
+ }
+
+ if (c.state != Channel.STATE_OPEN)
+ return copylen;
+
+ if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
+ {
+ int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
+ Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
+
+ increment = minFreeSpace - c.localWindow;
+ c.localWindow = minFreeSpace;
+ }
+
+ remoteID = c.remoteID; /* read while holding the lock */
+ localID = c.localID; /* read while holding the lock */
+ }
+
+ /*
+ * If a consumer reads stdout and stdin in parallel, we may end up with
+ * sending two msgWindowAdjust messages. Luckily, it
+ * does not matter in which order they arrive at the server.
+ */
+
+ if (increment > 0)
+ {
+ log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
+
+ synchronized (c.channelSendLock)
+ {
+ byte[] msg = c.msgWindowAdjust;
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
+ msg[1] = (byte) (remoteID >> 24);
+ msg[2] = (byte) (remoteID >> 16);
+ msg[3] = (byte) (remoteID >> 8);
+ msg[4] = (byte) (remoteID);
+ msg[5] = (byte) (increment >> 24);
+ msg[6] = (byte) (increment >> 16);
+ msg[7] = (byte) (increment >> 8);
+ msg[8] = (byte) (increment);
+
+ if (c.closeMessageSent == false)
+ tm.sendMessage(msg);
+ }
+ }
+
+ return copylen;
+ }
+ finally
+ {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ }
+
+ }
+
+ public void msgChannelData(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen <= 9)
+ throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
+
+ if (len != (msglen - 9))
+ throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
+ + len + ")");
+
+ log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
+
+ synchronized (c)
+ {
+ if (c.state == Channel.STATE_CLOSED)
+ return; // ignore
+
+ if (c.state != Channel.STATE_OPEN)
+ throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
+
+ if (c.localWindow < len)
+ throw new IOException("Remote sent too much data, does not fit into window.");
+
+ c.localWindow -= len;
+
+ System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
+ c.stdoutWritepos += len;
+
+ c.notifyAll();
+ }
+ }
+
+ public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 9)
+ throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ final long huge = 0xFFFFffffL; /* 2^32 - 1 */
+
+ c.remoteWindow += (windowChange & huge); /* avoid sign extension */
+
+ /* TODO - is this a good heuristic? */
+
+ if ((c.remoteWindow > huge))
+ c.remoteWindow = huge;
+
+ c.notifyAll();
+ }
+
+
+ log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
+ }
+
+ public void msgChannelOpen(byte[] msg, int msglen) throws IOException
+ {
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ String channelType = tr.readString();
+ int remoteID = tr.readUINT32(); /* sender channel */
+ int remoteWindow = tr.readUINT32(); /* initial window size */
+ int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
+
+ if ("x11".equals(channelType))
+ {
+ synchronized (x11_magic_cookies)
+ {
+ /* If we did not request X11 forwarding, then simply ignore this bogus request. */
+
+ if (x11_magic_cookies.size() == 0)
+ {
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+ Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ log.warning("Unexpected X11 request, denying it!");
+
+ return;
+ }
+ }
+
+ String remoteOriginatorAddress = tr.readString();
+ int remoteOriginatorPort = tr.readUINT32();
+
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ /*
+ * The open confirmation message will be sent from another thread
+ */
+
+ RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
+ rxat.setDaemon(true);
+ rxat.start();
+
+ return;
+ }
+
+ if ("forwarded-tcpip".equals(channelType))
+ {
+ String remoteConnectedAddress = tr.readString(); /* address that was connected */
+ int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
+ String remoteOriginatorAddress = tr.readString(); /* originator IP address */
+ int remoteOriginatorPort = tr.readUINT32(); /* originator port */
+
+ RemoteForwardingData rfd = null;
+
+ synchronized (remoteForwardings)
+ {
+ rfd = remoteForwardings.get(new Integer(remoteConnectedPort));
+ }
+
+ if (rfd == null)
+ {
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+ Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ "No thanks, unknown port in forwarded-tcpip request", "");
+
+ /* Always try to be polite. */
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ log.debug("Unexpected forwarded-tcpip request, denying it!");
+
+ return;
+ }
+
+ Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ /*
+ * The open confirmation message will be sent from another thread.
+ */
+
+ RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
+ remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
+
+ rat.setDaemon(true);
+ rat.start();
+
+ return;
+ }
+
+ if ((server_state != null) && ("session".equals(channelType)))
+ {
+ ServerConnectionCallback cb = null;
+
+ synchronized (server_state)
+ {
+ cb = server_state.cb_conn;
+ }
+
+ if (cb == null)
+ {
+ tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ "Sessions are currently not enabled", "en").getPayload());
+
+ return;
+ }
+
+ final Channel c = new Channel(this);
+
+ synchronized (c)
+ {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ c.state = Channel.STATE_OPEN;
+ c.ss = new ServerSessionImpl(c);
+ }
+
+ PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
+ c.localWindow, c.localMaxPacketSize);
+
+ tm.sendAsynchronousMessage(pcoc.getPayload());
+
+ c.ss.sscb = cb.acceptSession(c.ss);
+
+ return;
+ }
+
+ /* Tell the server that we have no idea what it is talking about */
+
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
+ "Unknown channel type", "");
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+
+ log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
+ }
+
+ /* Starts the given runnable in a foreground (non-daemon) thread */
+ private void runAsync(Runnable r)
+ {
+ Thread t = new Thread(r);
+ t.start();
+ }
+
+ public void msgChannelRequest(byte[] msg, int msglen) throws IOException
+ {
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ int id = tr.readUINT32();
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
+
+ ServerSessionImpl server_session = null;
+
+ if (server_state != null)
+ {
+ synchronized (c)
+ {
+ server_session = c.ss;
+ }
+ }
+
+ String type = tr.readString("US-ASCII");
+ boolean wantReply = tr.readBoolean();
+
+ log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
+
+ if (type.equals("exit-status"))
+ {
+ if (wantReply != false)
+ throw new IOException(
+ "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");
+
+ int exit_status = tr.readUINT32();
+
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ synchronized (c)
+ {
+ c.exit_status = new Integer(exit_status);
+ c.notifyAll();
+ }
+
+ log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
+
+ return;
+ }
+
+ if ((server_state == null) && (type.equals("exit-signal")))
+ {
+ if (wantReply != false)
+ throw new IOException(
+ "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");
+
+ String signame = tr.readString("US-ASCII");
+ tr.readBoolean();
+ tr.readString();
+ tr.readString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ synchronized (c)
+ {
+ c.exit_signal = signame;
+ c.notifyAll();
+ }
+
+ log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
+
+ return;
+ }
+
+ if ((server_session != null) && (type.equals("pty-req")))
+ {
+ PtySettings pty = new PtySettings();
+
+ pty.term = tr.readString();
+ pty.term_width_characters = tr.readUINT32();
+ pty.term_height_characters = tr.readUINT32();
+ pty.term_width_pixels = tr.readUINT32();
+ pty.term_height_pixels = tr.readUINT32();
+ pty.terminal_modes = tr.readByteString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ Runnable run_after_sending_success = null;
+
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if (sscb != null)
+ run_after_sending_success = sscb.requestPtyReq(server_session, pty);
+
+ if (wantReply)
+ {
+ if (run_after_sending_success != null)
+ {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else
+ {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if (run_after_sending_success != null)
+ {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ if ((server_session != null) && (type.equals("subsystem")))
+ {
+ String command = tr.readString();
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ Runnable run_after_sending_success = null;
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if (sscb != null)
+ run_after_sending_success = sscb.requestSubsystem(server_session, command);
+
+ if (wantReply)
+ {
+ if (run_after_sending_success != null)
+ {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else
+ {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if (run_after_sending_success != null)
+ {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ if ((server_session != null) && (type.equals("shell")))
+ {
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ Runnable run_after_sending_success = null;
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if (sscb != null)
+ run_after_sending_success = sscb.requestShell(server_session);
+
+ if (wantReply)
+ {
+ if (run_after_sending_success != null)
+ {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else
+ {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if (run_after_sending_success != null)
+ {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ if ((server_session != null) && (type.equals("exec")))
+ {
+ String command = tr.readString();
+
+ if (tr.remain() != 0)
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+
+ Runnable run_after_sending_success = null;
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if (sscb != null)
+ run_after_sending_success = sscb.requestExec(server_session, command);
+
+ if (wantReply)
+ {
+ if (run_after_sending_success != null)
+ {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else
+ {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if (run_after_sending_success != null)
+ {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ /* We simply ignore unknown channel requests, however, if the server wants a reply,
+ * then we signal that we have no idea what it is about.
+ */
+
+ if (wantReply)
+ {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+
+ log.debug("Channel request '" + type + "' is not known, ignoring it");
+ }
+
+ public void msgChannelEOF(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
+ }
+
+ public void msgChannelClose(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed("Close requested by remote");
+ c.closeMessageRecv = true;
+
+ removeChannel(c.localID);
+
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
+ }
+
+ public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.successCounter++;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
+ }
+
+ public void msgChannelFailure(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen != 5)
+ throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
+
+ synchronized (c)
+ {
+ c.failedCounter++;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
+ }
+
+ public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
+ {
+ PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
+
+ Channel c = getChannel(sm.recipientChannelID);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
+ + sm.recipientChannelID);
+
+ synchronized (c)
+ {
+ if (c.state != Channel.STATE_OPENING)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
+ + sm.recipientChannelID);
+
+ c.remoteID = sm.senderChannelID;
+ c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = sm.maxPacketSize;
+ c.state = Channel.STATE_OPEN;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
+ + sm.senderChannelID + ")");
+ }
+
+ public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
+ {
+ if (msglen < 5)
+ throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
+
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ int id = tr.readUINT32(); /* sender channel */
+
+ Channel c = getChannel(id);
+
+ if (c == null)
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
+
+ int reasonCode = tr.readUINT32();
+ String description = tr.readString("UTF-8");
+
+ String reasonCodeSymbolicName = null;
+
+ switch (reasonCode)
+ {
+ case 1:
+ reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
+ break;
+ case 2:
+ reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
+ break;
+ case 3:
+ reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
+ break;
+ case 4:
+ reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
+ break;
+ default:
+ reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
+ }
+
+ StringBuilder descriptionBuffer = new StringBuilder();
+ descriptionBuffer.append(description);
+
+ for (int i = 0; i < descriptionBuffer.length(); i++)
+ {
+ char cc = descriptionBuffer.charAt(i);
+
+ if ((cc >= 32) && (cc <= 126))
+ continue;
+ descriptionBuffer.setCharAt(i, '\uFFFD');
+ }
+
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
+ + descriptionBuffer.toString() + "')");
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
+ }
+
+ public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
+ {
+ /* Currently we do not support any kind of global request */
+
+ TypesReader tr = new TypesReader(msg, 0, msglen);
+
+ tr.readByte(); // skip packet type
+ String requestName = tr.readString();
+ boolean wantReply = tr.readBoolean();
+
+ if (wantReply)
+ {
+ byte[] reply_failure = new byte[1];
+ reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
+
+ tm.sendAsynchronousMessage(reply_failure);
+ }
+
+ /* We do not clean up the requestName String - that is OK for debug */
+
+ log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
+ }
+
+ public void msgGlobalSuccess() throws IOException
+ {
+ synchronized (channels)
+ {
+ globalSuccessCounter++;
+ channels.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_REQUEST_SUCCESS");
+ }
+
+ public void msgGlobalFailure() throws IOException
+ {
+ synchronized (channels)
+ {
+ globalFailedCounter++;
+ channels.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_REQUEST_FAILURE");
+ }
+
+ public void handleMessage(byte[] msg, int msglen) throws IOException
+ {
+ if (msg == null)
+ {
+
+ log.debug("HandleMessage: got shutdown");
+
+ synchronized (listenerThreads)
+ {
+ for (IChannelWorkerThread lat : listenerThreads)
+ {
+ lat.stopWorking();
+ }
+ listenerThreadsAllowed = false;
+ }
+
+ synchronized (channels)
+ {
+ shutdown = true;
+
+ for (Channel c : channels)
+ {
+ synchronized (c)
+ {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed("The connection is being shutdown");
+ c.closeMessageRecv = true; /*
+ * You never know, perhaps
+ * we are waiting for a
+ * pending close message
+ * from the server...
+ */
+ c.notifyAll();
+ }
+ }
+
+ channels.clear();
+ channels.notifyAll(); /* Notify global response waiters */
+ return;
+ }
+ }
+
+ switch (msg[0])
+ {
+ case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+ msgChannelOpenConfirmation(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
+ msgChannelWindowAdjust(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_DATA:
+ msgChannelData(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
+ msgChannelExtendedData(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_REQUEST:
+ msgChannelRequest(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_EOF:
+ msgChannelEOF(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_OPEN:
+ msgChannelOpen(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_CLOSE:
+ msgChannelClose(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_SUCCESS:
+ msgChannelSuccess(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_FAILURE:
+ msgChannelFailure(msg, msglen);
+ break;
+ case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
+ msgChannelOpenFailure(msg, msglen);
+ break;
+ case Packets.SSH_MSG_GLOBAL_REQUEST:
+ msgGlobalRequest(msg, msglen);
+ break;
+ case Packets.SSH_MSG_REQUEST_SUCCESS:
+ msgGlobalSuccess();
+ break;
+ case Packets.SSH_MSG_REQUEST_FAILURE:
+ msgGlobalFailure();
+ break;
+ default:
+ throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
+ }
+ }
+}