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;
}
--- /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";
- }
-
-
}
<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>
+++ /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.api;
-
-public interface YangStoreListenerRegistration extends AutoCloseable {
-
- @Override
- void close();
-}
*/
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");
- }
}
<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
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);
}
public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedFormatException {
JsonParser parser = new JsonParser();
-
+
JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
if (!rootElement.isJsonObject()) {
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 UnsupportedFormatException("Json Object should contain one element");
if (firstElementInArray.isJsonObject()) {
return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
}
- throw new UnsupportedFormatException("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 UnsupportedFormatException("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(getNamespaceFrom(rootObjectName),
getLocalNameFrom(rootObjectName));
}
return firstNode;
}
-
+
private void addChildToParent(String childName, JsonElement childType, CompositeNodeWrapper parent) {
if (childType.isJsonObject()) {
CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFrom(childName),
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(getNamespaceFrom(childName), getLocalNameFrom(childName), null);
- } else {
- child = new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), value);
- }
- parent.addValue(child);
+ parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), value));
}
}
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) {
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 false;
+ return cancel;
}
@Override
public boolean isCancelled() {
- return false;
+ return isCancelled;
}
@Override
public boolean isDone() {
- return false;
+ return isDone;
}
@Override
public RpcResult<TransactionStatus> get() throws InterruptedException, ExecutionException {
- return null;
+ return result;
}
@Override
public RpcResult<TransactionStatus> get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
- return null;
+ 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);
+ }
+
+ }
+
+}
import com.google.gson.JsonSyntaxException;
-public class FromJsonToCompositeNode {
+public class FromJsonToCompositeNodeTest {
- private static Logger LOG = LoggerFactory.getLogger(FromJsonToCompositeNode.class);
+ 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:data:types");
+ "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:data:types");
+ "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);
+
}
/**
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;
}
- private void simpleTest(String jsonPath, String yangPath, String topLevelElementName, String namespace) {
+ /**
+ * 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);
+ dataSchemaNode = TestUtils.obtainSchemaFromYang(yangPath, moduleName);
} catch (FileNotFoundException e) {
LOG.error(e.getMessage());
assertTrue(false);
throws WebApplicationException {
JsonToCompositeNodeProvider jsonToCompositeNodeProvider = JsonToCompositeNodeProvider.INSTANCE;
- InputStream jsonStream = FromJsonToCompositeNode.class.getResourceAsStream(jsonPath);
+ InputStream jsonStream = FromJsonToCompositeNodeTest.class.getResourceAsStream(jsonPath);
try {
CompositeNode compositeNode = jsonToCompositeNodeProvider
.readFrom(null, null, null, null, null, jsonStream);
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.slf4j.*;
-public class FromXmlToCompositeNode {
- private static Logger LOG = LoggerFactory.getLogger(FromXmlToCompositeNode.class);
+public class FromXmlToCompositeNodeTest {
+ private static final Logger LOG = LoggerFactory.getLogger(FromXmlToCompositeNodeTest.class);
/**
* top level element represents container. second level element is list with
private CompositeNode compositeContainerFromXml(String xmlPath, boolean dummyNamespaces) {
XmlToCompositeNodeProvider xmlToCompositeNodeProvider = XmlToCompositeNodeProvider.INSTANCE;
try {
- InputStream xmlStream = FromXmlToCompositeNode.class.getResourceAsStream(xmlPath);
+ InputStream xmlStream = FromXmlToCompositeNodeTest.class.getResourceAsStream(xmlPath);
CompositeNode compositeNode = xmlToCompositeNodeProvider.readFrom(null, null, null, null, null, xmlStream);
if (dummyNamespaces) {
try {
import java.net.*;
import java.sql.Date;
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.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+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.XmlTreeBuilder;
import org.opendaylight.yangtools.yang.model.api.*;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
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();
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(
- new DummyFuture());
+ when(broker.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(future);
restconf.setControllerContext(controllerContext);
restconf.setBroker(broker);
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.ExecutionException;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
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.client.ClientConfig;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
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);
}
@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("/config/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
- } catch (UnsupportedEncodingException | URISyntaxException e) {
- e.printStackTrace();
- }
- InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
- final CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
- when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(new Future<RpcResult<TransactionStatus>>() {
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return false;
- }
- @Override
- public boolean isCancelled() {
- return false;
- }
- @Override
- public boolean isDone() {
- return false;
- }
- @Override
- public RpcResult<TransactionStatus> get() throws InterruptedException, ExecutionException {
- return null;
- }
- @Override
- public RpcResult<TransactionStatus> get(long timeout, TimeUnit unit) throws InterruptedException,
- ExecutionException, TimeoutException {
- return null;
- }
- });
+ public void testBadFormatXmlToCompositeNodeProvider() throws UnsupportedEncodingException, URISyntaxException {
+ String uri = createUri("/operations/", "ietf-interfaces:interfaces/interface/eth0");
- DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
- DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
- xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
- Document doc = docBuilder.parse(xmlStream);
+ Response response = target(uri).request(MediaTypes.API + RestconfService.XML).post(
+ Entity.entity("<SimpleNode/>", MEDIA_TYPE));
+ assertEquals(400, 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")));
- assertEquals(204, response.getStatus());
+ response = target(uri).request(MediaTypes.API + RestconfService.XML).post(
+ Entity.entity("<SimpleNode>", MEDIA_TYPE));
+ assertEquals(400, 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 testXmlToCompositeNode404NotFound() throws UnsupportedEncodingException, URISyntaxException {
+ String uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
- 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());
+ when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(null);
- response = target(uri.toASCIIString()).request(MediaTypes.API + RestconfService.XML).post(
- Entity.entity("<SimpleNode>", new MediaType("application", "vnd.yang.api+xml")));
- assertEquals(400, response.getStatus());
+ Response response = target(uri).request(MediaTypes.API+RestconfService.XML).get();
+ assertEquals(404, response.getStatus());
}
@Test
- public void testXmlToCompositeNode404NotFound() {
- 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 testRpcResultCommitedToStatusCodes() 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.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);
- when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(null);
+ 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());
- Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).get();
- assertEquals(404, response.getStatus());
+ 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());
+
+ 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 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);
+
+ 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());
+
+ 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":[45,55,66]
+ }
+}
\ No newline at end of file
-module simple-data-types {
- namespace "simple:data:types";
+module simple-container-yang {
+ namespace "simple:container:yang";
prefix "smpdtp";
revision 2013-11-12 {
-module simple-data-types {
- namespace "simple:data:types";
+module simple-list-yang1 {
+ namespace "simple:list:yang1";
- prefix "smpdtp";
+ prefix "smplstyg";
revision 2013-11-12 {
}
--- /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
@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 {