<arphandler.version>0.5.2-SNAPSHOT</arphandler.version>
<forwarding.staticrouting>0.5.2-SNAPSHOT</forwarding.staticrouting>
<samples.loadbalancer>0.5.2-SNAPSHOT</samples.loadbalancer>
- <config.version>0.2.4-SNAPSHOT</config.version>
- <netconf.version>0.2.4-SNAPSHOT</netconf.version>
+ <config.version>0.2.5-SNAPSHOT</config.version>
+ <netconf.version>0.2.5-SNAPSHOT</netconf.version>
<mdsal.version>1.1-SNAPSHOT</mdsal.version>
<containermanager.version>0.5.2-SNAPSHOT</containermanager.version>
<containermanager.it.version>0.5.2-SNAPSHOT</containermanager.it.version>
<switchmanager.api.version>0.7.1-SNAPSHOT</switchmanager.api.version>
<connectionmanager.version>0.1.2-SNAPSHOT</connectionmanager.version>
- <sal.version>0.7.1-SNAPSHOT</sal.version>
+ <sal.version>0.8.1-SNAPSHOT</sal.version>
<sal.networkconfiguration.version>0.0.3-SNAPSHOT</sal.networkconfiguration.version>
<sal.connection.version>0.1.2-SNAPSHOT</sal.connection.version>
<networkconfig.bridgedomain.northbound.version>0.0.3-SNAPSHOT</networkconfig.bridgedomain.northbound.version>
<java.version.target>1.7</java.version.target>
<!-- enforcer version -->
<enforcer.version>1.3.1</enforcer.version>
+ <xtend.version>2.4.3</xtend.version>
+ <xtend.dstdir>${project.build.directory}/generated-sources/xtend-gen</xtend.dstdir>
</properties>
<dependencyManagement>
</dependency>
<!-- md-sal -->
+ <dependency>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>org.eclipse.xtend.lib</artifactId>
+ <version>${xtend.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common</artifactId>
<artifactId>config-persister-impl</artifactId>
<version>${netconf.version}</version>
</dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>ietf-netconf-monitoring</artifactId>
- <version>${netconf.version}</version>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>ietf-netconf-monitoring-extension</artifactId>
- <version>${netconf.version}</version>
- </dependency>
<!-- threadpool -->
<dependency>
<artifactId>maven-release-plugin</artifactId>
<version>${releaseplugin.version}</version>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ </plugin>
<!-- Ignore/Execute plugin execution -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<testTarget>${java.version.target}</testTarget>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.eclipse.xtend</groupId>
+ <artifactId>xtend-maven-plugin</artifactId>
+ <version>${xtend.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${xtend.dstdir}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</pluginManagement>
</build>
}
@Override
+ @SuppressWarnings("unchecked")
protected final void channelRead0(final ChannelHandlerContext ctx, final Object msg) {
LOG.debug("Message was received: {}", msg);
handleMessage((M) msg);
}
@Override
+ @SuppressWarnings("unchecked")
public final void channelRead(final ChannelHandlerContext ctx, final Object msg) {
LOG.debug("Negotiation read invoked on channel {}", channel);
try {
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>config-api</artifactId>
* transaction was committed after creating this transaction. Clients can create
* new transaction and merge the changes.
*/
-public class ConflictingVersionException extends RuntimeException {
+public class ConflictingVersionException extends Exception {
private static final long serialVersionUID = 1L;
public ConflictingVersionException() {
@ConstructorProperties(QNAME_ATTR_NAME)
public IdentityAttributeRef(String qNameOfIdentity) {
- if (qNameOfIdentity == null)
+ if (qNameOfIdentity == null) {
throw new NullPointerException("Parameter " + QNAME_ATTR_NAME + " is null");
+ }
this.qNameOfIdentity = qNameOfIdentity;
}
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof IdentityAttributeRef)) return false;
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof IdentityAttributeRef)) {
+ return false;
+ }
IdentityAttributeRef that = (IdentityAttributeRef) o;
- if (!qNameOfIdentity.equals(that.qNameOfIdentity)) return false;
+ if (!qNameOfIdentity.equals(that.qNameOfIdentity)) {
+ return false;
+ }
return true;
}
private final String attributeName;
public JmxAttribute(String attributeName) {
- if (attributeName == null)
+ if (attributeName == null) {
throw new NullPointerException("Parameter 'attributeName' is null");
+ }
this.attributeName = attributeName;
}
@Override
public boolean equals(Object o) {
- if (this == o)
+ if (this == o) {
return true;
- if (o == null || getClass() != o.getClass())
+ }
+ if (o == null || getClass() != o.getClass()) {
return false;
+ }
JmxAttribute that = (JmxAttribute) o;
if (attributeName != null ? !attributeName.equals(that.attributeName)
- : that.attributeName != null)
+ : that.attributeName != null) {
return false;
+ }
return true;
}
private final String factoryName, instanceName;
public ModuleIdentifier(String factoryName, String instanceName) {
- if (factoryName == null)
- throw new IllegalArgumentException(
- "Parameter 'factoryName' is null");
- if (instanceName == null)
- throw new IllegalArgumentException(
- "Parameter 'instanceName' is null");
+ if (factoryName == null) {
+ throw new IllegalArgumentException("Parameter 'factoryName' is null");
+ }
+ if (instanceName == null) {
+ throw new IllegalArgumentException("Parameter 'instanceName' is null");
+ }
this.factoryName = factoryName;
this.instanceName = instanceName;
}
@Override
public boolean equals(Object o) {
- if (this == o)
+ if (this == o) {
return true;
- if (o == null || getClass() != o.getClass())
+ }
+ if (o == null || getClass() != o.getClass()) {
return false;
+ }
ModuleIdentifier that = (ModuleIdentifier) o;
- if (!factoryName.equals(that.factoryName))
+ if (!factoryName.equals(that.factoryName)) {
return false;
- if (!instanceName.equals(that.instanceName))
+ }
+ if (!instanceName.equals(that.instanceName)) {
return false;
+ }
return true;
}
/**
* This exception is not intended to be used while implementing modules,
- * itaggregates validation exceptions and sends them back to the user.
+ * it aggregates validation exceptions and sends them back to the user.
+ * Use {@link org.opendaylight.controller.config.api.JmxAttributeValidationException} for
+ * validating modules instead.
*/
-public class ValidationException extends RuntimeException {
+public class ValidationException extends Exception {
private static final long serialVersionUID = -6072893219820274247L;
private final Map<String/* module name */, Map<String/* instance name */, ExceptionMessageWithStackTrace>> failedValidations;
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
ExceptionMessageWithStackTrace other = (ExceptionMessageWithStackTrace) obj;
if (message == null) {
- if (other.message != null)
+ if (other.message != null) {
return false;
- } else if (!message.equals(other.message))
+ }
+ } else if (!message.equals(other.message)) {
return false;
+ }
if (stackTrace == null) {
- if (other.stackTrace != null)
+ if (other.stackTrace != null) {
return false;
- } else if (!stackTrace.equals(other.stackTrace))
+ }
+ } else if (!stackTrace.equals(other.stackTrace)) {
return false;
+ }
return true;
}
*/
package org.opendaylight.controller.config.api.jmx;
+import javax.annotation.concurrent.Immutable;
+import javax.management.ObjectName;
import java.beans.ConstructorProperties;
import java.util.Collections;
import java.util.List;
-import javax.annotation.concurrent.Immutable;
-import javax.management.ObjectName;
-
@Immutable
public class CommitStatus {
private final List<ObjectName> newInstances, reusedInstances,
recreatedInstances;
/**
- *
- * @param newInstances
- * newly created instances
- * @param reusedInstances
- * reused instances
- * @param recreatedInstances
- * recreated instances
+ * @param newInstances newly created instances
+ * @param reusedInstances reused instances
+ * @param recreatedInstances recreated instances
*/
- @ConstructorProperties({ "newInstances", "reusedInstances",
- "recreatedInstances" })
+ @ConstructorProperties({"newInstances", "reusedInstances",
+ "recreatedInstances"})
public CommitStatus(List<ObjectName> newInstances,
- List<ObjectName> reusedInstances,
- List<ObjectName> recreatedInstances) {
+ List<ObjectName> reusedInstances,
+ List<ObjectName> recreatedInstances) {
this.newInstances = Collections.unmodifiableList(newInstances);
this.reusedInstances = Collections.unmodifiableList(reusedInstances);
this.recreatedInstances = Collections
}
/**
- *
* @return list of objectNames representing newly created instances
*/
public List<ObjectName> getNewInstances() {
}
/**
- *
* @return list of objectNames representing reused instances
*/
public List<ObjectName> getReusedInstances() {
}
/**
- *
* @return list of objectNames representing recreated instances
*/
public List<ObjectName> getRecreatedInstances() {
result = prime
* result
+ ((recreatedInstances == null) ? 0 : recreatedInstances
- .hashCode());
+ .hashCode());
result = prime * result
+ ((reusedInstances == null) ? 0 : reusedInstances.hashCode());
return result;
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
CommitStatus other = (CommitStatus) obj;
if (newInstances == null) {
- if (other.newInstances != null)
+ if (other.newInstances != null) {
return false;
- } else if (!newInstances.equals(other.newInstances))
+ }
+ } else if (!newInstances.equals(other.newInstances)) {
return false;
+ }
if (recreatedInstances == null) {
- if (other.recreatedInstances != null)
+ if (other.recreatedInstances != null) {
return false;
- } else if (!recreatedInstances.equals(other.recreatedInstances))
+ }
+ } else if (!recreatedInstances.equals(other.recreatedInstances)) {
return false;
+ }
if (reusedInstances == null) {
- if (other.reusedInstances != null)
+ if (other.reusedInstances != null) {
return false;
- } else if (!reusedInstances.equals(other.reusedInstances))
+ }
+ } else if (!reusedInstances.equals(other.reusedInstances)) {
return false;
+ }
return true;
}
import org.opendaylight.controller.config.api.jmx.constants.ConfigRegistryConstants;
import javax.annotation.concurrent.ThreadSafe;
+import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import java.util.Arrays;
import java.util.HashMap;
public static ObjectName createON(String on) {
try {
return new ObjectName(on);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ } catch (MalformedObjectNameException e) {
+ throw new IllegalArgumentException(e);
}
}
Hashtable<String, String> table = new Hashtable<>(attribs);
try {
return new ObjectName(domain, table);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ } catch (MalformedObjectNameException e) {
+ throw new IllegalArgumentException(e);
}
}
public static String getServiceQName(ObjectName objectName) {
checkType(objectName, TYPE_SERVICE_REFERENCE);
String quoted = objectName.getKeyProperty(SERVICE_QNAME_KEY);
- String result = unquoteAndUnescape(objectName, quoted);
- return result;
+ return unquoteAndUnescape(objectName, quoted);
}
// ObjectName supports quotation and ignores tokens like =, but fails to ignore ? sign.
}
}
- public static void checkTypeOneOf(ObjectName objectName, String ... types) {
- for(String type: types) {
+ public static void checkTypeOneOf(ObjectName objectName, String... types) {
+ for (String type : types) {
if (type.equals(objectName.getKeyProperty(TYPE_KEY))) {
return;
}
public static ObjectName createModulePattern(String moduleName,
String instanceName) {
- if (moduleName == null)
+ if (moduleName == null) {
moduleName = "*";
- if (instanceName == null)
+ }
+ if (instanceName == null) {
instanceName = "*";
+ }
// do not return object names containing transaction name
ObjectName namePattern = ObjectNameUtil
.createON(ObjectNameUtil.ON_DOMAIN + ":"
String expectedType) {
checkType(objectName, expectedType);
String factoryName = getFactoryName(objectName);
- if (factoryName == null)
+ if (factoryName == null) {
throw new IllegalArgumentException(
"ObjectName does not contain module name");
+ }
String instanceName = getInstanceName(objectName);
- if (instanceName == null)
+ if (instanceName == null) {
throw new IllegalArgumentException(
"ObjectName does not contain instance name");
+ }
return new ModuleIdentifier(factoryName, instanceName);
}
*/
package org.opendaylight.controller.config.api.jmx.constants;
+import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
public class ConfigRegistryConstants {
public static final ObjectName OBJECT_NAME = createONWithDomainAndType(TYPE_CONFIG_REGISTRY);
- public static String GET_AVAILABLE_MODULE_NAMES_ATTRIBUT_NAME = "AvailableModuleNames";
+ public static final String GET_AVAILABLE_MODULE_NAMES_ATTRIBUT_NAME = "AvailableModuleNames";
public static ObjectName createONWithDomainAndType(String type) {
return createON(ON_DOMAIN, TYPE_KEY, type);
public static ObjectName createON(String name, String key, String value) {
try {
return new ObjectName(name, key, value);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ } catch (MalformedObjectNameException e) {
+ throw new IllegalArgumentException(e);
}
}
*/
package org.opendaylight.controller.config.spi;
-import javax.annotation.concurrent.NotThreadSafe;
-
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.yangtools.concepts.Identifiable;
+import javax.annotation.concurrent.NotThreadSafe;
+
/**
* Represents one service that is to be configured. These methods need to be
* ConfigBeans.
* <p>
* In order to guide dependency resolution, the setter method should be
- * annotated with {@link RequireInterface}.
+ * annotated with {@link org.opendaylight.controller.config.api.annotations.RequireInterface}.
* </p>
* <p>
* Thread safety note: implementations of this interface are not required to be
* Returns 'live' object that was configured using this object. It is
* allowed to call this method only after all ConfigBeans were validated. In
* this method new resources might be opened or old instance might be
- * modified. Note that when obtaining dependent Module using
- * {@link org.opendaylight.controller.config.api.DependencyResolver#validateDependency(Class, javax.management.ObjectName, String)}
- * a proxy will be created that will disallow calling this method before
- * second commit phase begins.
+ * modified. This method must be implemented so that it returns same
+ * result for a single transaction. Since Module is created per transaction
+ * this means that it must be safe to cache result of first call.
+ *
*
* @return closeable instance: After bundle update the factory might be able
* to copy old configuration into new one without being able to cast
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-manager</artifactId>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
import javax.annotation.concurrent.Immutable;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
/**
* Structure obtained during first phase commit, contains destroyed modules from
*/
package org.opendaylight.controller.config.manager.impl;
+import com.google.common.collect.Maps;
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.jmx.CommitStatus;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
private List<ModuleFactory> lastListOfFactories = Collections.emptyList();
@GuardedBy("this") // switched in every 2ndPC
- private CloseableServiceReferenceReadableRegistry readableSRRegistry = ServiceReferenceRegistryImpl.createInitialSRLookupRegistry();
+ private CloseableServiceReferenceReadableRegistry readableSRRegistry = ServiceReferenceRegistryImpl.createInitialSRLookupRegistry();
// constructor
public ConfigRegistryImpl(ModuleFactoriesResolver resolver,
- MBeanServer configMBeanServer, CodecRegistry codecRegistry) {
+ MBeanServer configMBeanServer, CodecRegistry codecRegistry) {
this(resolver, configMBeanServer,
new BaseJMXRegistrator(configMBeanServer), codecRegistry);
}
// constructor
public ConfigRegistryImpl(ModuleFactoriesResolver resolver,
- MBeanServer configMBeanServer,
- BaseJMXRegistrator baseJMXRegistrator, CodecRegistry codecRegistry) {
+ MBeanServer configMBeanServer,
+ BaseJMXRegistrator baseJMXRegistrator, CodecRegistry codecRegistry) {
this.resolver = resolver;
this.beanToOsgiServiceManager = new BeanToOsgiServiceManager();
this.configMBeanServer = configMBeanServer;
throw new IllegalStateException(e);
}
transactionController.copyExistingModulesAndProcessFactoryDiff(currentConfig.getEntries(), lastListOfFactories);
- transactionsHolder.add(transactionName, transactionController);
+ transactionsHolder.add(transactionName, transactionController, txLookupRegistry);
return transactionController;
}
* {@inheritDoc}
*/
@Override
+ @SuppressWarnings("PMD.AvoidCatchingThrowable")
public synchronized CommitStatus commitConfig(ObjectName transactionControllerON)
throws ConflictingVersionException, ValidationException {
final String transactionName = ObjectNameUtil
logger.trace("About to commit {}. Current parentVersion: {}, versionCounter {}", transactionName, version, versionCounter);
// find ConfigTransactionController
- Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder.getCurrentTransactions();
- ConfigTransactionControllerInternal configTransactionController = transactions.get(transactionName);
- if (configTransactionController == null) {
+ Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = transactionsHolder.getCurrentTransactions();
+ Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry = transactions.get(transactionName);
+ if (configTransactionControllerEntry == null) {
throw new IllegalArgumentException(String.format(
"Transaction with name '%s' not found", transactionName));
}
+ ConfigTransactionControllerInternal configTransactionController = configTransactionControllerEntry.getKey();
// check optimistic lock
if (version != configTransactionController.getParentVersion()) {
throw new ConflictingVersionException(
lastListOfFactories = Collections.unmodifiableList(configTransactionController.getCurrentlyRegisteredFactories());
// non recoverable from here:
try {
- return secondPhaseCommit(
- configTransactionController, commitInfo);
+ return secondPhaseCommit(configTransactionController, commitInfo, configTransactionControllerEntry.getValue());
} catch (Throwable t) { // some libs throw Errors: e.g.
- // javax.xml.ws.spi.FactoryFinder$ConfigurationError
+ // javax.xml.ws.spi.FactoryFinder$ConfigurationError
isHealthy = false;
logger.error("Configuration Transaction failed on 2PC, server is unhealthy", t);
if (t instanceof RuntimeException) {
}
private CommitStatus secondPhaseCommit(ConfigTransactionControllerInternal configTransactionController,
- CommitInfo commitInfo) {
+ CommitInfo commitInfo, ConfigTransactionLookupRegistry txLookupRegistry) {
// close instances which were destroyed by the user, including
// (hopefully) runtime beans
for (DestroyedModule toBeDestroyed : commitInfo
.getDestroyedFromPreviousTransactions()) {
toBeDestroyed.close(); // closes instance (which should close
- // runtime jmx registrator),
+ // runtime jmx registrator),
// also closes osgi registration and ModuleJMXRegistrator
// registration
currentConfig.remove(toBeDestroyed.getIdentifier());
.getRuntimeBeanRegistrator();
}
// set runtime jmx registrator if required
- Module module = entry.getModule();
+ Module module = entry.getProxiedModule();
if (module instanceof RuntimeBeanRegistratorAwareModule) {
((RuntimeBeanRegistratorAwareModule) module)
.setRuntimeBeanRegistrator(runtimeBeanRegistrator);
}
// can register runtime beans
- List<ModuleIdentifier> orderedModuleIdentifiers = configTransactionController
- .secondPhaseCommit();
+ List<ModuleIdentifier> orderedModuleIdentifiers = configTransactionController.secondPhaseCommit();
+ txLookupRegistry.close();
+ configTransactionController.close();
// copy configuration to read only mode
List<ObjectName> newInstances = new LinkedList<>();
for (ModuleIdentifier moduleIdentifier : orderedModuleIdentifiers) {
ModuleInternalTransactionalInfo entry = commitInfo.getCommitted()
.get(moduleIdentifier);
- if (entry == null)
+ if (entry == null) {
throw new NullPointerException("Module not found "
+ moduleIdentifier);
- Module module = entry.getModule();
+ }
+
ObjectName primaryReadOnlyON = ObjectNameUtil
.createReadOnlyModuleON(moduleIdentifier);
.createModuleJMXRegistrator();
OsgiRegistration osgiRegistration = null;
+ AutoCloseable instance = entry.getProxiedModule().getInstance();
if (entry.hasOldModule()) {
ModuleInternalInfo oldInternalInfo = entry.getOldInternalInfo();
DynamicReadableWrapper oldReadableConfigBean = oldInternalInfo.getReadableModule();
currentConfig.remove(entry.getIdentifier());
// test if old instance == new instance
- if (oldReadableConfigBean.getInstance().equals(module.getInstance())) {
+ if (oldReadableConfigBean.getInstance().equals(instance)) {
// reused old instance:
// wrap in readable dynamic mbean
reusedInstances.add(primaryReadOnlyON);
// wrap in readable dynamic mbean
newInstances.add(primaryReadOnlyON);
}
+ Module realModule = entry.getRealModule();
DynamicReadableWrapper newReadableConfigBean = new DynamicReadableWrapper(
- module, module.getInstance(), moduleIdentifier,
+ realModule, instance, moduleIdentifier,
registryMBeanServer, configMBeanServer);
// register to JMX
// register to OSGi
if (osgiRegistration == null) {
ModuleFactory moduleFactory = entry.getModuleFactory();
- if(moduleFactory != null) {
+ if (moduleFactory != null) {
BundleContext bc = configTransactionController.
getModuleFactoryBundleContext(moduleFactory.getImplementationName());
- osgiRegistration = beanToOsgiServiceManager.registerToOsgi(module.getClass(),
+ osgiRegistration = beanToOsgiServiceManager.registerToOsgi(realModule.getClass(),
newReadableConfigBean.getInstance(), entry.getIdentifier(), bc);
} else {
throw new NullPointerException(entry.getIdentifier().getFactoryName() + " ModuleFactory not found.");
runtimeBeanRegistrator, newModuleJMXRegistrator,
orderingIdx, entry.isDefaultBean());
- newConfigEntries.put(module, newInfo);
+ newConfigEntries.put(realModule, newInfo);
orderingIdx++;
}
currentConfig.addAll(newConfigEntries.values());
*/
@Override
public synchronized List<ObjectName> getOpenConfigs() {
- Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+ Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = transactionsHolder
.getCurrentTransactions();
List<ObjectName> result = new ArrayList<>(transactions.size());
- for (ConfigTransactionControllerInternal configTransactionController : transactions
+ for (Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry : transactions
.values()) {
- result.add(configTransactionController.getControllerObjectName());
+ result.add(configTransactionControllerEntry.getKey().getControllerObjectName());
}
return result;
}
@Override
public synchronized void close() {
// abort transactions
- Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+ Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = transactionsHolder
.getCurrentTransactions();
- for (ConfigTransactionControllerInternal configTransactionController : transactions
+ for (Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry : transactions
.values()) {
+
+ ConfigTransactionControllerInternal configTransactionController = configTransactionControllerEntry.getKey();
try {
+ configTransactionControllerEntry.getValue().close();
configTransactionController.abortConfig();
} catch (RuntimeException e) {
logger.warn("Ignoring exception while aborting {}",
*/
@Override
public Set<ObjectName> lookupConfigBeans(String moduleName,
- String instanceName) {
+ String instanceName) {
ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
instanceName);
return baseJMXRegistrator.queryNames(namePattern, null);
*/
@Override
public Set<ObjectName> lookupRuntimeBeans(String moduleName,
- String instanceName) {
- if (moduleName == null)
+ String instanceName) {
+ if (moduleName == null) {
moduleName = "*";
- if (instanceName == null)
+ }
+ if (instanceName == null) {
instanceName = "*";
+ }
ObjectName namePattern = ObjectNameUtil.createRuntimeBeanPattern(
moduleName, instanceName);
return baseJMXRegistrator.queryNames(namePattern, null);
* {@link ConfigTransactionControllerInternal} instances, because platform
* MBeanServer transforms mbeans into another representation. Map is cleaned
* every time current transactions are requested.
- *
*/
@GuardedBy("ConfigRegistryImpl.this")
- private final Map<String /* transactionName */, ConfigTransactionControllerInternal> transactions = new HashMap<>();
+ private final Map<String /* transactionName */,
+ Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = new HashMap<>();
/**
* Can only be called from within synchronized method.
*/
public void add(String transactionName,
- ConfigTransactionControllerInternal transactionController) {
+ ConfigTransactionControllerInternal transactionController, ConfigTransactionLookupRegistry txLookupRegistry) {
Object oldValue = transactions.put(transactionName,
- transactionController);
+ Maps.immutableEntry(transactionController, txLookupRegistry));
if (oldValue != null) {
throw new IllegalStateException(
"Error: two transactions with same name");
*
* @return current view on transactions map.
*/
- public Map<String, ConfigTransactionControllerInternal> getCurrentTransactions() {
+ public Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> getCurrentTransactions() {
// first, remove closed transaction
- for (Iterator<Entry<String, ConfigTransactionControllerInternal>> it = transactions
- .entrySet().iterator(); it.hasNext();) {
- Entry<String, ConfigTransactionControllerInternal> entry = it
+ for (Iterator<Entry<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>>> it = transactions
+ .entrySet().iterator(); it.hasNext(); ) {
+ Entry<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> entry = it
.next();
- if (entry.getValue().isClosed()) {
+ if (entry.getValue().getKey().isClosed()) {
it.remove();
}
}
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
class ConfigTransactionControllerImpl implements
ConfigTransactionControllerInternal,
ConfigTransactionControllerImplMXBean,
- Identifiable<TransactionIdentifier>{
+ Identifiable<TransactionIdentifier> {
private static final Logger logger = LoggerFactory.getLogger(ConfigTransactionControllerImpl.class);
private final ConfigTransactionLookupRegistry txLookupRegistry;
this.currentlyRegisteredFactories = currentlyRegisteredFactories;
this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories);
this.transactionStatus = new TransactionStatus();
- this.dependencyResolverManager = new DependencyResolverManager(transactionName, transactionStatus, writableSRRegistry, codecRegistry);
+ this.dependencyResolverManager = new DependencyResolverManager(txLookupRegistry.getTransactionIdentifier(),
+ transactionStatus, writableSRRegistry, codecRegistry);
this.transactionsMBeanServer = transactionsMBeanServer;
this.configMBeanServer = configMBeanServer;
this.blankTransaction = blankTransaction;
List<ModuleFactory> toBeAdded = new ArrayList<>();
List<ModuleFactory> toBeRemoved = new ArrayList<>();
- for(ModuleFactory moduleFactory: factoriesHolder.getModuleFactories()) {
- if (oldSet.contains(moduleFactory) == false){
+ for (ModuleFactory moduleFactory : factoriesHolder.getModuleFactories()) {
+ if (oldSet.contains(moduleFactory) == false) {
toBeAdded.add(moduleFactory);
}
}
- for(ModuleFactory moduleFactory: lastListOfFactories){
+ for (ModuleFactory moduleFactory : lastListOfFactories) {
if (newSet.contains(moduleFactory) == false) {
toBeRemoved.add(moduleFactory);
}
}
// remove modules belonging to removed factories
- for(ModuleFactory removedFactory: toBeRemoved){
+ for (ModuleFactory removedFactory : toBeRemoved) {
List<ModuleIdentifier> modulesOfRemovedFactory = dependencyResolverManager.findAllByFactory(removedFactory);
for (ModuleIdentifier name : modulesOfRemovedFactory) {
destroyModule(name);
@Override
public synchronized ObjectName createModule(String factoryName,
- String instanceName) throws InstanceAlreadyExistsException {
+ String instanceName) throws InstanceAlreadyExistsException {
transactionStatus.checkNotCommitStarted();
transactionStatus.checkNotAborted();
throws InstanceAlreadyExistsException {
logger.debug("Adding module {} to transaction {}", moduleIdentifier, this);
- if (moduleIdentifier.equals(module.getIdentifier())==false) {
+ if (moduleIdentifier.equals(module.getIdentifier()) == false) {
throw new IllegalStateException("Incorrect name reported by module. Expected "
- + moduleIdentifier + ", got " + module.getIdentifier());
+ + moduleIdentifier + ", got " + module.getIdentifier());
}
- if (dependencyResolver.getIdentifier().equals(moduleIdentifier) == false ) {
+ if (dependencyResolver.getIdentifier().equals(moduleIdentifier) == false) {
throw new IllegalStateException("Incorrect name reported by dependency resolver. Expected "
+ moduleIdentifier + ", got " + dependencyResolver.getIdentifier());
}
// put wrapper to jmx
TransactionModuleJMXRegistration transactionModuleJMXRegistration = getTxModuleJMXRegistrator()
.registerMBean(writableDynamicWrapper, writableON);
- ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
+
+ dependencyResolverManager.put(
moduleIdentifier, module, moduleFactory,
maybeOldConfigBeanInfo, transactionModuleJMXRegistration, isDefaultBean);
-
- dependencyResolverManager.put(moduleInternalTransactionalInfo);
return writableON;
}
// first remove refNames, it checks for objectname existence
try {
writableSRRegistry.removeServiceReferences(
- ObjectNameUtil.createTransactionModuleON(getTransactionName(),moduleIdentifier));
+ ObjectNameUtil.createTransactionModuleON(getTransactionName(), moduleIdentifier));
} catch (InstanceNotFoundException e) {
logger.error("Possible code error: cannot find {} in {}", moduleIdentifier, writableSRRegistry);
throw new IllegalStateException("Possible code error: cannot find " + moduleIdentifier, e);
@Override
public synchronized void validateConfig() throws ValidationException {
- if (configBeanModificationDisabled.get())
+ if (configBeanModificationDisabled.get()) {
throw new IllegalStateException("Cannot start validation");
+ }
configBeanModificationDisabled.set(true);
try {
validate_noLocks();
logger.error("Commit failed on {} in transaction {}", name,
getTransactionIdentifier(), e);
internalAbort();
- throw new RuntimeException(
+ throw new IllegalStateException(
format("Error - getInstance() failed for %s in transaction %s",
name, getTransactionIdentifier()), e);
}
logger.trace("Committed configuration {}", getTransactionIdentifier());
transactionStatus.setCommitted();
- // unregister this and all modules from jmx
- close();
return dependencyResolverManager.getSortedModuleIdentifiers();
}
}
public void close() {
- //FIXME: should not close object that was retrieved in constructor, a wrapper object should do that perhaps
- txLookupRegistry.close();
+ dependencyResolverManager.close();
}
@Override
return writableSRRegistry;
}
+ @Override
public TransactionIdentifier getTransactionIdentifier() {
return txLookupRegistry.getTransactionIdentifier();
}
* and {@link ConfigRegistryImpl} (consumer).
*/
interface ConfigTransactionControllerInternal extends
- ConfigTransactionControllerImplMXBean {
+ ConfigTransactionControllerImplMXBean, AutoCloseable {
ServiceReferenceWritableRegistry getWritableRegistry();
+ TransactionIdentifier getTransactionIdentifier();
+
+ @Override
+ void close();
}
--- /dev/null
+package org.opendaylight.controller.config.manager.impl;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+import java.util.concurrent.TimeUnit;
+
+public class DeadlockMonitor implements AutoCloseable {
+ private static final Logger logger = LoggerFactory.getLogger(DeadlockMonitorRunnable.class);
+
+ private static final long WARN_AFTER_MILLIS = 5000;
+
+ private final TransactionIdentifier transactionIdentifier;
+ private final DeadlockMonitorRunnable thread;
+ @GuardedBy("this")
+ private ModuleIdentifierWithNanos moduleIdentifierWithNanos = new ModuleIdentifierWithNanos();
+
+ public DeadlockMonitor(TransactionIdentifier transactionIdentifier) {
+ this.transactionIdentifier = transactionIdentifier;
+ thread = new DeadlockMonitorRunnable();
+ thread.start();
+ }
+
+ public synchronized void setCurrentlyInstantiatedModule(ModuleIdentifier currentlyInstantiatedModule) {
+ this.moduleIdentifierWithNanos = new ModuleIdentifierWithNanos(currentlyInstantiatedModule);
+ }
+
+ public boolean isAlive() {
+ return thread.isAlive();
+ }
+
+ @Override
+ public void close() {
+ thread.interrupt();
+ }
+
+ @Override
+ public String toString() {
+ return "DeadlockMonitor{" + transactionIdentifier + '}';
+ }
+
+ private class DeadlockMonitorRunnable extends Thread {
+
+ private DeadlockMonitorRunnable() {
+ super(DeadlockMonitor.this.toString());
+ }
+
+ @Override
+ public void run() {
+ ModuleIdentifierWithNanos old = new ModuleIdentifierWithNanos(); // null moduleId
+ while (this.isInterrupted() == false) {
+ ModuleIdentifierWithNanos copy = new ModuleIdentifierWithNanos(DeadlockMonitor.this.moduleIdentifierWithNanos);
+ if (old.moduleIdentifier == null) {
+ // started
+ old = copy;
+ } else if (old.moduleIdentifier != null && old.equals(copy)) {
+ // is the getInstance() running longer than WARN_AFTER_MILLIS ?
+ long runningTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - copy.nanoTime);
+ if (runningTime > WARN_AFTER_MILLIS) {
+ logger.warn("{} did not finish after {} ms", copy.moduleIdentifier, runningTime);
+ }
+ }
+ try {
+ sleep(1000);
+ } catch (InterruptedException e) {
+ interrupt();
+ }
+ }
+ logger.trace("Exiting {}", this);
+ }
+
+ @Override
+ public String toString() {
+ return "DeadLockMonitorRunnable{" + transactionIdentifier + "}";
+ }
+ }
+
+ private class ModuleIdentifierWithNanos {
+ @Nullable
+ private final ModuleIdentifier moduleIdentifier;
+ private final long nanoTime;
+
+ private ModuleIdentifierWithNanos() {
+ moduleIdentifier = null;
+ nanoTime = System.nanoTime();
+ }
+
+ private ModuleIdentifierWithNanos(ModuleIdentifier moduleIdentifier) {
+ this.moduleIdentifier = moduleIdentifier;
+ nanoTime = System.nanoTime();
+ }
+
+ private ModuleIdentifierWithNanos(ModuleIdentifierWithNanos copy) {
+ moduleIdentifier = copy.moduleIdentifier;
+ nanoTime = copy.nanoTime;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ModuleIdentifierWithNanos that = (ModuleIdentifierWithNanos) o;
+
+ if (nanoTime != that.nanoTime) return false;
+ if (moduleIdentifier != null ? !moduleIdentifier.equals(that.moduleIdentifier) : that.moduleIdentifier != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = moduleIdentifier != null ? moduleIdentifier.hashCode() : 0;
+ result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
+ return result;
+ }
+ }
+}
import javax.annotation.Nullable;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
this.serviceReferenceRegistrator = serviceReferenceRegistratorFactory.create();
- Map<String, Set<String /* QName */>> factoryNamesToQNames = new HashMap<>();
+ Map<String, Set<String /* QName */>> modifiableFactoryNamesToQNames = new HashMap<>();
Set<ServiceInterfaceAnnotation> allAnnotations = new HashSet<>();
Set<String /* qName */> allQNames = new HashSet<>();
for (Entry<String, ModuleFactory> entry : factories.entrySet()) {
}
allAnnotations.addAll(siAnnotations);
allQNames.addAll(qNames);
- factoryNamesToQNames.put(entry.getKey(), Collections.unmodifiableSet(qNames));
+ modifiableFactoryNamesToQNames.put(entry.getKey(), Collections.unmodifiableSet(qNames));
}
- this.factoryNamesToQNames = Collections.unmodifiableMap(factoryNamesToQNames);
+ this.factoryNamesToQNames = Collections.unmodifiableMap(modifiableFactoryNamesToQNames);
this.allQNames = Collections.unmodifiableSet(allQNames);
// fill namespacesToAnnotations
- Map<String /* namespace */, Map<String /* localName */, ServiceInterfaceAnnotation>> namespacesToAnnotations =
+ Map<String /* namespace */, Map<String /* localName */, ServiceInterfaceAnnotation>> modifiableNamespacesToAnnotations =
new HashMap<>();
for (ServiceInterfaceAnnotation sia : allAnnotations) {
- Map<String, ServiceInterfaceAnnotation> ofNamespace = namespacesToAnnotations.get(sia.namespace());
+ Map<String, ServiceInterfaceAnnotation> ofNamespace = modifiableNamespacesToAnnotations.get(sia.namespace());
if (ofNamespace == null) {
ofNamespace = new HashMap<>();
- namespacesToAnnotations.put(sia.namespace(), ofNamespace);
+ modifiableNamespacesToAnnotations.put(sia.namespace(), ofNamespace);
}
if (ofNamespace.containsKey(sia.localName())) {
logger.error("Cannot construct namespacesToAnnotations map, conflict between local names in {}, offending local name: {}, map so far {}",
- sia.namespace(), sia.localName(), namespacesToAnnotations);
+ sia.namespace(), sia.localName(), modifiableNamespacesToAnnotations);
throw new IllegalArgumentException("Conflict between local names in " + sia.namespace() + " : " + sia.localName());
}
ofNamespace.put(sia.localName(), sia);
}
- this.namespacesToAnnotations = Collections.unmodifiableMap(namespacesToAnnotations);
+ this.namespacesToAnnotations = Collections.unmodifiableMap(modifiableNamespacesToAnnotations);
// copy refNames
logger.trace("factoryNamesToQNames:{}", this.factoryNamesToQNames);
}
@Override
public boolean equals(Object o) {
- if (this == o)
+ if (this == o) {
return true;
- if (o == null || getClass() != o.getClass())
+ }
+ if (o == null || getClass() != o.getClass()) {
return false;
+ }
TransactionIdentifier that = (TransactionIdentifier) o;
- if (name != null ? !name.equals(that.name) : that.name != null)
+ if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
+ }
return true;
}
}
public synchronized void checkNotCommitStarted() {
- if (secondPhaseCommitStarted == true)
+ if (secondPhaseCommitStarted == true) {
throw new IllegalStateException("Commit was triggered");
+ }
}
public synchronized void checkCommitStarted() {
- if (secondPhaseCommitStarted == false)
+ if (secondPhaseCommitStarted == false) {
throw new IllegalStateException("Commit was not triggered");
+ }
}
public synchronized void checkNotAborted() {
- if (aborted == true)
+ if (aborted == true) {
throw new IllegalStateException("Configuration was aborted");
+ }
}
public synchronized void checkNotCommitted() {
* during validation. Tracks dependencies for ordering purposes.
*/
final class DependencyResolverImpl implements DependencyResolver,
- Comparable<DependencyResolverImpl> {
+ Comparable<DependencyResolverImpl> {
private static final Logger logger = LoggerFactory.getLogger(DependencyResolverImpl.class);
private final ModulesHolder modulesHolder;
throw new NullPointerException(
"Parameter 'expectedServiceInterface' is null");
}
- if (jmxAttribute == null)
+ if (jmxAttribute == null) {
throw new NullPointerException("Parameter 'jmxAttribute' is null");
+ }
JmxAttributeValidationException.checkNotNull(dependentReadOnlyON,
"is null, expected dependency implementing "
+ expectedServiceInterface, jmxAttribute);
-
// check that objectName belongs to this transaction - this should be
// stripped
// in DynamicWritableWrapper
}
}
- // transalate from serviceref to module ON
+ // translate from serviceref to module ON
private ObjectName translateServiceRefIfPossible(ObjectName dependentReadOnlyON) {
if (ObjectNameUtil.isServiceReference(dependentReadOnlyON)) {
String serviceQName = ObjectNameUtil.getServiceQName(dependentReadOnlyON);
//TODO: check for cycles
@Override
public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentReadOnlyON,
- JmxAttribute jmxAttribute) {
+ JmxAttribute jmxAttribute) {
if (expectedType == null || dependentReadOnlyON == null || jmxAttribute == null) {
throw new IllegalArgumentException(format(
"Null parameters not allowed, got %s %s %s", expectedType,
throw new JmxAttributeValidationException(message, jmxAttribute);
}
try {
- T result = expectedType.cast(instance);
- return result;
+ return expectedType.cast(instance);
} catch (ClassCastException e) {
String message = format(
"Instance cannot be cast to expected type. Instance class is %s , "
IdentityCodec<?> identityCodec = codecRegistry.getIdentityCodec();
Class<? extends BaseIdentity> deserialized = identityCodec.deserialize(qName);
if (deserialized == null) {
- throw new RuntimeException("Unable to retrieve identity class for " + qName + ", null response from "
+ throw new IllegalStateException("Unable to retrieve identity class for " + qName + ", null response from "
+ codecRegistry);
}
if (expectedBaseClass.isAssignableFrom(deserialized)) {
public <T extends BaseIdentity> void validateIdentity(IdentityAttributeRef identityRef, Class<T> expectedBaseClass, JmxAttribute jmxAttribute) {
try {
resolveIdentity(identityRef, expectedBaseClass);
- } catch(Exception e) {
+ } catch (Exception e) {
throw JmxAttributeValidationException.wrap(e, jmxAttribute);
}
}
return maxDependencyDepth;
}
- public void countMaxDependencyDepth(DependencyResolverManager manager) {
+ void countMaxDependencyDepth(DependencyResolverManager manager) {
transactionStatus.checkCommitted();
if (maxDependencyDepth == null) {
maxDependencyDepth = getMaxDepth(this, manager,
}
private static int getMaxDepth(DependencyResolverImpl impl,
- DependencyResolverManager manager,
- LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
+ DependencyResolverManager manager,
+ LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
int maxDepth = 0;
LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
chainForDetectingCycles);
public ModuleIdentifier getIdentifier() {
return name;
}
+
}
*/
package org.opendaylight.controller.config.manager.impl.dependencyresolver;
+import com.google.common.reflect.AbstractInvocationHandler;
+import com.google.common.reflect.Reflection;
import org.opendaylight.controller.config.api.DependencyResolver;
import org.opendaylight.controller.config.api.DependencyResolverFactory;
import org.opendaylight.controller.config.api.JmxAttribute;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
import org.opendaylight.controller.config.manager.impl.CommitInfo;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.DeadlockMonitor;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
import javax.annotation.concurrent.GuardedBy;
import javax.management.InstanceAlreadyExistsException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import static com.google.common.base.Preconditions.checkState;
+
/**
* Holds information about modules being created and destroyed within this
* transaction. Observes usage of DependencyResolver within modules to figure
* out dependency tree.
*/
-public class DependencyResolverManager implements TransactionHolder, DependencyResolverFactory {
+public class DependencyResolverManager implements DependencyResolverFactory, AutoCloseable {
@GuardedBy("this")
private final Map<ModuleIdentifier, DependencyResolverImpl> moduleIdentifiersToDependencyResolverMap = new HashMap<>();
private final ModulesHolder modulesHolder;
private final TransactionStatus transactionStatus;
private final ServiceReferenceReadableRegistry readableRegistry;
private final CodecRegistry codecRegistry;
+ private final DeadlockMonitor deadlockMonitor;
- public DependencyResolverManager(String transactionName,
+ public DependencyResolverManager(TransactionIdentifier transactionIdentifier,
TransactionStatus transactionStatus, ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry) {
- this.modulesHolder = new ModulesHolder(transactionName);
+ this.modulesHolder = new ModulesHolder(transactionIdentifier);
this.transactionStatus = transactionStatus;
this.readableRegistry = readableRegistry;
this.codecRegistry = codecRegistry;
+ this.deadlockMonitor = new DeadlockMonitor(transactionIdentifier);
}
@Override
return result;
}
- @Override
public ModuleInternalTransactionalInfo destroyModule(
ModuleIdentifier moduleIdentifier) {
transactionStatus.checkNotCommitted();
}
// protect write access
- @Override
+
public void put(
- ModuleInternalTransactionalInfo moduleInternalTransactionalInfo) {
+ final ModuleIdentifier moduleIdentifier,
+ final Module module,
+ ModuleFactory moduleFactory,
+ ModuleInternalInfo maybeOldInternalInfo,
+ TransactionModuleJMXRegistration transactionModuleJMXRegistration,
+ boolean isDefaultBean) {
transactionStatus.checkNotCommitted();
+
+ Class<? extends Module> moduleClass = Module.class;
+ if (module instanceof RuntimeBeanRegistratorAwareModule) {
+ moduleClass = RuntimeBeanRegistratorAwareModule.class;
+ }
+ Module proxiedModule = Reflection.newProxy(moduleClass, new AbstractInvocationHandler() {
+ // optimization: subsequent calls to getInstance MUST return the same value during transaction,
+ // so it is safe to cache the response
+ private Object cachedInstance;
+
+ @Override
+ protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
+ boolean isGetInstance = method.getName().equals("getInstance");
+ if (isGetInstance) {
+ if (cachedInstance != null) {
+ return cachedInstance;
+ }
+
+ checkState(deadlockMonitor.isAlive(), "Deadlock monitor is not alive");
+ deadlockMonitor.setCurrentlyInstantiatedModule(moduleIdentifier);
+ }
+ try {
+ Object response = method.invoke(module, args);
+ if (isGetInstance) {
+ cachedInstance = response;
+ }
+ return response;
+ } catch(InvocationTargetException e) {
+ throw e.getCause();
+ } finally {
+ if (isGetInstance) {
+ deadlockMonitor.setCurrentlyInstantiatedModule(null);
+ }
+ }
+ }
+ });
+
+
+ ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
+ moduleIdentifier, proxiedModule, moduleFactory,
+ maybeOldInternalInfo, transactionModuleJMXRegistration, isDefaultBean, module);
modulesHolder.put(moduleInternalTransactionalInfo);
}
// wrapped methods:
- @Override
public CommitInfo toCommitInfo() {
return modulesHolder.toCommitInfo();
}
- @Override
public Module findModule(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting) {
+ JmxAttribute jmxAttributeForReporting) {
return modulesHolder.findModule(moduleIdentifier,
jmxAttributeForReporting);
}
- @Override
public ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(ModuleIdentifier moduleIdentifier) {
return modulesHolder.findModuleInternalTransactionalInfo(moduleIdentifier);
}
- @Override
public ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting) {
+ JmxAttribute jmxAttributeForReporting) {
return modulesHolder.findModuleFactory(moduleIdentifier,
jmxAttributeForReporting);
}
- @Override
public Map<ModuleIdentifier, Module> getAllModules() {
return modulesHolder.getAllModules();
}
- @Override
public void assertNotExists(ModuleIdentifier moduleIdentifier)
throws InstanceAlreadyExistsException {
modulesHolder.assertNotExists(moduleIdentifier);
public List<ModuleIdentifier> findAllByFactory(ModuleFactory factory) {
List<ModuleIdentifier> result = new ArrayList<>();
- for( ModuleInternalTransactionalInfo info : modulesHolder.getAllInfos()) {
+ for (ModuleInternalTransactionalInfo info : modulesHolder.getAllInfos()) {
if (factory.equals(info.getModuleFactory())) {
result.add(info.getIdentifier());
}
}
return result;
}
+
+ public void close() {
+ deadlockMonitor.close();
+ }
+
}
* 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.manager.impl;
+package org.opendaylight.controller.config.manager.impl.dependencyresolver;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
private final OsgiRegistration osgiRegistration;
private final int orderingIdx;
- DestroyedModule(ModuleIdentifier identifier, AutoCloseable instance,
+ public DestroyedModule(ModuleIdentifier identifier, AutoCloseable instance,
ModuleJMXRegistrator oldJMXRegistrator,
OsgiRegistration osgiRegistration, int orderingIdx) {
this.identifier = identifier;
* 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.manager.impl;
-
-import javax.annotation.Nullable;
+package org.opendaylight.controller.config.manager.impl.dependencyresolver;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
-import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator
- .TransactionModuleJMXRegistration;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.yangtools.concepts.Identifiable;
+import javax.annotation.Nullable;
+
public class ModuleInternalTransactionalInfo implements Identifiable<ModuleIdentifier> {
private final ModuleIdentifier name;
- private final Module module;
+ private final Module proxiedModule, realModule;
private final ModuleFactory moduleFactory;
@Nullable
private final ModuleInternalInfo maybeOldInternalInfo;
private final TransactionModuleJMXRegistration transactionModuleJMXRegistration;
private final boolean isDefaultBean;
- ModuleInternalTransactionalInfo(ModuleIdentifier name, Module module,
- ModuleFactory moduleFactory,
- ModuleInternalInfo maybeOldInternalInfo,
- TransactionModuleJMXRegistration transactionModuleJMXRegistration,
- boolean isDefaultBean) {
+ public ModuleInternalTransactionalInfo(ModuleIdentifier name, Module proxiedModule,
+ ModuleFactory moduleFactory,
+ ModuleInternalInfo maybeOldInternalInfo,
+ TransactionModuleJMXRegistration transactionModuleJMXRegistration,
+ boolean isDefaultBean, Module realModule) {
this.name = name;
- this.module = module;
+ this.proxiedModule = proxiedModule;
this.moduleFactory = moduleFactory;
this.maybeOldInternalInfo = maybeOldInternalInfo;
this.transactionModuleJMXRegistration = transactionModuleJMXRegistration;
this.isDefaultBean = isDefaultBean;
+ this.realModule = realModule;
}
}
- public Module getModule() {
- return module;
+ public Module getProxiedModule() {
+ return proxiedModule;
}
public ModuleFactory getModuleFactory() {
@Nullable
public ModuleInternalInfo getOldInternalInfo() {
- if (maybeOldInternalInfo == null)
+ if (maybeOldInternalInfo == null) {
throw new NullPointerException();
+ }
return maybeOldInternalInfo;
}
public boolean isDefaultBean() {
return isDefaultBean;
}
+
+ public Module getRealModule() {
+ return realModule;
+ }
}
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.manager.impl.CommitInfo;
-import org.opendaylight.controller.config.manager.impl.DestroyedModule;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
/**
* Represents modules to be committed.
*/
-class ModulesHolder implements TransactionHolder {
- private final String transactionName;
+class ModulesHolder {
+ private final TransactionIdentifier transactionIdentifier;
@GuardedBy("this")
private final Map<ModuleIdentifier, ModuleInternalTransactionalInfo> commitMap = new HashMap<>();
@GuardedBy("this")
private final Set<ModuleInternalTransactionalInfo> unorderedDestroyedFromPreviousTransactions = new HashSet<>();
- ModulesHolder(String transactionName) {
- this.transactionName = transactionName;
+ ModulesHolder(TransactionIdentifier transactionIdentifier) {
+ this.transactionIdentifier = transactionIdentifier;
}
- @Override
+
public CommitInfo toCommitInfo() {
List<DestroyedModule> orderedDestroyedFromPreviousTransactions = new ArrayList<>(
unorderedDestroyedFromPreviousTransactions.size());
.get(moduleIdentifier);
JmxAttributeValidationException.checkNotNull(
moduleInternalTransactionalInfo, "Module " + moduleIdentifier
- + "" + " not found in transaction " + transactionName,
+ + "" + " not found in transaction " + transactionIdentifier,
jmxAttributeForReporting);
return moduleInternalTransactionalInfo;
}
- @Override
public Module findModule(ModuleIdentifier moduleIdentifier,
JmxAttribute jmxAttributeForReporting) {
return findModuleInternalTransactionalInfo(moduleIdentifier,
- jmxAttributeForReporting).getModule();
+ jmxAttributeForReporting).getProxiedModule();
}
- @Override
public ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
JmxAttribute jmxAttributeForReporting) {
return findModuleInternalTransactionalInfo(moduleIdentifier,
jmxAttributeForReporting).getModuleFactory();
}
- @Override
public Map<ModuleIdentifier, Module> getAllModules() {
Map<ModuleIdentifier, Module> result = new HashMap<>();
for (ModuleInternalTransactionalInfo entry : commitMap.values()) {
ModuleIdentifier name = entry.getIdentifier();
- result.put(name, entry.getModule());
+ result.put(name, entry.getProxiedModule());
}
return result;
}
- @Override
public void put(
ModuleInternalTransactionalInfo moduleInternalTransactionalInfo) {
commitMap.put(moduleInternalTransactionalInfo.getIdentifier(),
moduleInternalTransactionalInfo);
}
- @Override
public ModuleInternalTransactionalInfo destroyModule(
ModuleIdentifier moduleIdentifier) {
ModuleInternalTransactionalInfo found = commitMap.remove(moduleIdentifier);
return found;
}
- @Override
public void assertNotExists(ModuleIdentifier moduleIdentifier)
throws InstanceAlreadyExistsException {
if (commitMap.containsKey(moduleIdentifier)) {
return commitMap.values();
}
- @Override
public ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(ModuleIdentifier moduleIdentifier) {
ModuleInternalTransactionalInfo found = commitMap.get(moduleIdentifier);
if (found == null) {
+++ /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.manager.impl.dependencyresolver;
-
-import org.opendaylight.controller.config.api.JmxAttribute;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.opendaylight.controller.config.manager.impl.CommitInfo;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
-import org.opendaylight.controller.config.spi.Module;
-import org.opendaylight.controller.config.spi.ModuleFactory;
-
-import javax.management.InstanceAlreadyExistsException;
-import java.util.Map;
-
-interface TransactionHolder {
- CommitInfo toCommitInfo();
-
- Module findModule(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting);
-
- ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting);
-
- Map<ModuleIdentifier, Module> getAllModules();
-
- void put(ModuleInternalTransactionalInfo moduleInternalTransactionalInfo);
-
- ModuleInternalTransactionalInfo destroyModule(
- ModuleIdentifier moduleIdentifier);
-
- void assertNotExists(ModuleIdentifier moduleIdentifier)
- throws InstanceAlreadyExistsException;
-
- ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(ModuleIdentifier moduleIdentifier);
-
-}
* requests (getAttribute, setAttribute, invoke) into the actual instance, but
* provides additional functionality - namely it disallows setting attribute on
* a read only wrapper.
- *
*/
abstract class AbstractDynamicWrapper implements DynamicMBeanModuleWrapper {
private static final Logger logger = LoggerFactory
protected final MBeanServer internalServer;
public AbstractDynamicWrapper(Module module, boolean writable,
- ModuleIdentifier moduleIdentifier,
- ObjectName thisWrapperObjectName, MBeanOperationInfo[] dOperations,
- MBeanServer internalServer, MBeanServer configMBeanServer) {
+ ModuleIdentifier moduleIdentifier,
+ ObjectName thisWrapperObjectName, MBeanOperationInfo[] dOperations,
+ MBeanServer internalServer, MBeanServer configMBeanServer) {
this.writable = writable;
this.module = module;
* case unregister the module and remove listener.
*/
private final NotificationListener registerActualModule(Module module,
- final ObjectName thisWrapperObjectName,
- final ObjectName objectNameInternal,
- final MBeanServer internalServer,
- final MBeanServer configMBeanServer) {
+ final ObjectName thisWrapperObjectName,
+ final ObjectName objectNameInternal,
+ final MBeanServer internalServer,
+ final MBeanServer configMBeanServer) {
try {
internalServer.registerMBean(module, objectNameInternal);
public void handleNotification(Notification n, Object handback) {
if (n instanceof MBeanServerNotification
&& n.getType()
- .equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
+ .equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
if (((MBeanServerNotification) n).getMBeanName().equals(
thisWrapperObjectName)) {
try {
}
private static MBeanInfo generateMBeanInfo(String className, Module module,
- Map<String, AttributeHolder> attributeHolderMap,
- MBeanOperationInfo[] dOperations, Set<Class<?>> jmxInterfaces) {
+ Map<String, AttributeHolder> attributeHolderMap,
+ MBeanOperationInfo[] dOperations, Set<Class<?>> jmxInterfaces) {
String dDescription = findDescription(module.getClass(), jmxInterfaces);
MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[0];
// inspect all exported interfaces ending with MXBean, extract getters &
// setters into attribute holder
private static Map<String, AttributeHolder> buildMBeanInfo(Module module,
- boolean writable, ModuleIdentifier moduleIdentifier,
- Set<Class<?>> jmxInterfaces, MBeanServer internalServer,
- ObjectName internalObjectName) {
+ boolean writable, ModuleIdentifier moduleIdentifier,
+ Set<Class<?>> jmxInterfaces, MBeanServer internalServer,
+ ObjectName internalObjectName) {
// internal variables for describing MBean elements
Set<Method> methods = new HashSet<>();
}
AttributeHolder attributeHolder = new AttributeHolder(
attribName, module, attributeMap.get(attribName)
- .getType(), writable, ifc, description);
+ .getType(), writable, ifc, description);
attributeHolderMap.put(attribName, attributeHolder);
}
}
}
- if(isDependencyListAttr(attributeName, obj)) {
+ if (isDependencyListAttr(attributeName, obj)) {
obj = fixDependencyListAttribute(obj);
}
}
private Object fixDependencyListAttribute(Object attribute) {
- if(attribute.getClass().isArray() == false)
+ if (attribute.getClass().isArray() == false) {
throw new IllegalArgumentException("Unexpected attribute type, should be an array, but was " + attribute.getClass());
+ }
for (int i = 0; i < Array.getLength(attribute); i++) {
Object on = Array.get(attribute, i);
- if(on instanceof ObjectName == false)
+ if (on instanceof ObjectName == false) {
throw new IllegalArgumentException("Unexpected attribute type, should be an ObjectName, but was " + on.getClass());
+ }
on = fixObjectName((ObjectName) on);
Array.set(attribute, i, on);
}
private boolean isDependencyListAttr(String attributeName, Object attribute) {
- if (attributeHolderMap.containsKey(attributeName) == false)
+ if (attributeHolderMap.containsKey(attributeName) == false) {
return false;
+ }
AttributeHolder attributeHolder = attributeHolderMap.get(attributeName);
}
protected ObjectName fixObjectName(ObjectName on) {
- if (!ObjectNameUtil.ON_DOMAIN.equals(on.getDomain()))
+ if (!ObjectNameUtil.ON_DOMAIN.equals(on.getDomain())) {
throw new IllegalArgumentException("Wrong domain, expected "
+ ObjectNameUtil.ON_DOMAIN + " setter on " + on);
+ }
// if on contains transaction name, remove it
String transactionName = ObjectNameUtil.getTransactionName(on);
- if (transactionName != null)
+ if (transactionName != null) {
return ObjectNameUtil.withoutTransactionName(on);
- else
+ } else {
return on;
+ }
}
@Override
*/
package org.opendaylight.controller.config.manager.impl.dynamicmbean;
+import org.opendaylight.controller.config.api.annotations.Description;
+
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
-import org.opendaylight.controller.config.api.annotations.Description;
-
-class AnnotationsHelper {
+public class AnnotationsHelper {
/**
* Look for annotation specified by annotationType on method. First observe
}
public AttributeHolder(String name, Object object, String returnType,
- boolean writable,
- @Nullable RequireInterface requireInterfaceAnnotation,
- String description) {
+ boolean writable,
+ @Nullable RequireInterface requireInterfaceAnnotation,
+ String description) {
if (name == null) {
throw new NullPointerException();
}
/**
* @return annotation if setter sets ObjectName or ObjectName[], and is
- * annotated. Return null otherwise.
+ * annotated. Return null otherwise.
*/
RequireInterface getRequireInterfaceOrNull() {
return requireInterfaceAnnotation;
* @param setter
* @param jmxInterfaces
* @return empty string if no annotation is found, or list of descriptions
- * separated by newline
+ * separated by newline
*/
static String findDescription(Method setter, Set<Class<?>> jmxInterfaces) {
List<Description> descriptions = AnnotationsHelper
*
* @param setter
* @param inspectedInterfaces
- * @throws IllegalStateException
- * if more than one value is specified by found annotations
- * @throws IllegalArgumentException
- * if set of exported interfaces contains non interface type
* @return null if no annotation is found, otherwise return the annotation
+ * @throws IllegalStateException if more than one value is specified by found annotations
+ * @throws IllegalArgumentException if set of exported interfaces contains non interface type
*/
static RequireInterface findRequireInterfaceAnnotation(final Method setter,
- Set<Class<?>> inspectedInterfaces) {
+ Set<Class<?>> inspectedInterfaces) {
// only allow setX(ObjectName y) or setX(ObjectName[] y) or setX(List<ObjectName> y) to continue
- if (setter.getParameterTypes().length > 1)
+ if (setter.getParameterTypes().length > 1) {
return null;
- if(PERMITTED_PARAMETER_TYPES_FOR_DEPENDENCY_SETTER.contains(setter.getParameterTypes()[0]) == false)
+ }
+ if (PERMITTED_PARAMETER_TYPES_FOR_DEPENDENCY_SETTER.contains(setter.getParameterTypes()[0]) == false) {
return null;
+ }
List<RequireInterface> foundRequireInterfaces = AnnotationsHelper
.findMethodAnnotationInSuperClassesAndIfcs(setter, RequireInterface.class, inspectedInterfaces);
try {
validate();
} catch (Exception e) {
- throw ValidationException.createForSingleException(
- moduleIdentifier, e);
+
+ throw new MBeanException(ValidationException.createForSingleException(
+ moduleIdentifier, e));
}
return Void.TYPE;
}
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import java.util.Map.Entry;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Collections;
+import java.util.Map.Entry;
+import java.util.Set;
import java.util.TreeSet;
-import java.util.Collection;
-import java.util.ArrayList;
/**
* Hold sorted ConfigMBeanFactories by their module names. Check that module
* names are globally unique.
*/
public class HierarchicalConfigMBeanFactoriesHolder {
- private static final Logger logger = LoggerFactory
- .getLogger(HierarchicalConfigMBeanFactoriesHolder.class);
private final Map<String, Map.Entry<ModuleFactory, BundleContext>> moduleNamesToConfigBeanFactories;
private final Set<String> moduleNames;
*/
package org.opendaylight.controller.config.manager.impl.jmx;
-import java.io.Closeable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.annotation.concurrent.GuardedBy;
import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
import javax.management.JMX;
+import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.QueryExp;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
public class InternalJMXRegistrator implements Closeable {
private static final Logger logger = LoggerFactory
private final ObjectName on;
InternalJMXRegistration(InternalJMXRegistrator internalJMXRegistrator,
- ObjectName on) {
+ ObjectName on) {
this.internalJMXRegistrator = internalJMXRegistrator;
this.on = on;
}
private final List<InternalJMXRegistrator> children = new ArrayList<>();
public synchronized InternalJMXRegistration registerMBean(Object object,
- ObjectName on) throws InstanceAlreadyExistsException {
+ ObjectName on) throws InstanceAlreadyExistsException {
try {
configMBeanServer.registerMBean(object, on);
- } catch (InstanceAlreadyExistsException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
+ } catch (MBeanRegistrationException | NotCompliantMBeanException e) {
+ throw new IllegalStateException(e);
}
registeredObjectNames.add(on);
return new InternalJMXRegistration(this, on);
private synchronized void unregisterMBean(ObjectName on) {
// first check that on was registered using this instance
boolean removed = registeredObjectNames.remove(on);
- if (!removed)
- throw new IllegalStateException(
- "Cannot unregister - ObjectName not found in 'registeredObjectNames': "
- + on);
+ if (!removed) {
+ throw new IllegalStateException("Cannot unregister - ObjectName not found in 'registeredObjectNames': " + on);
+ }
try {
configMBeanServer.unregisterMBean(on);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ } catch (InstanceNotFoundException | MBeanRegistrationException e) {
+ throw new IllegalStateException(e);
}
}
}
public <T> T newMBeanProxy(ObjectName objectName, Class<T> interfaceClass,
- boolean notificationBroadcaster) {
+ boolean notificationBroadcaster) {
return JMX.newMBeanProxy(configMBeanServer, objectName, interfaceClass,
notificationBroadcaster);
}
}
public <T> T newMXBeanProxy(ObjectName objectName, Class<T> interfaceClass,
- boolean notificationBroadcaster) {
+ boolean notificationBroadcaster) {
return JMX.newMXBeanProxy(configMBeanServer, objectName,
interfaceClass, notificationBroadcaster);
}
@Override
public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
ServiceReference that = (ServiceReference) o;
- if (!refName.equals(that.refName)) return false;
- if (!serviceInterfaceName.equals(that.serviceInterfaceName)) return false;
+ if (!refName.equals(that.refName)) {
+ return false;
+ }
+ if (!serviceInterfaceName.equals(that.serviceInterfaceName)) {
+ return false;
+ }
return true;
}
*/
package org.opendaylight.controller.config.manager.impl.jmx;
-import java.io.Closeable;
-import java.util.Set;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.jmx.InternalJMXRegistrator.InternalJMXRegistration;
import javax.management.InstanceAlreadyExistsException;
import javax.management.ObjectName;
import javax.management.QueryExp;
-
-import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
-import org.opendaylight.controller.config.manager.impl.jmx.InternalJMXRegistrator.InternalJMXRegistration;
+import java.io.Closeable;
+import java.util.Set;
/**
* Contains constraints on passed {@link ObjectName} parameters. Only allow (un)
private final String transactionName;
TransactionJMXRegistrator(InternalJMXRegistrator internalJMXRegistrator,
- String transactionName) {
+ String transactionName) {
this.childJMXRegistrator = internalJMXRegistrator.createChild();
this.transactionName = transactionName;
}
public TransactionJMXRegistration registerMBean(Object object, ObjectName on)
throws InstanceAlreadyExistsException {
- if (!transactionName.equals(ObjectNameUtil.getTransactionName(on)))
+ if (!transactionName.equals(ObjectNameUtil.getTransactionName(on))) {
throw new IllegalArgumentException(
"Transaction name mismatch between expected "
+ transactionName + " " + "and " + on);
+ }
ObjectNameUtil.checkType(on, ObjectNameUtil.TYPE_CONFIG_TRANSACTION);
return new TransactionJMXRegistration(
childJMXRegistrator.registerMBean(object, on));
package org.opendaylight.controller.config.manager.impl.osgi;
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.ConfigRegistryImpl;
import org.opendaylight.controller.config.spi.ModuleFactory;
synchronized void blankTransaction() {
// race condition check: config-persister might push new configuration while server is starting up.
ConflictingVersionException lastException = null;
- for (int i = 0; i < 10; i++) {
+ int maxAttempts = 10;
+ for (int i = 0; i < maxAttempts; i++) {
try {
// create transaction
boolean blankTransaction = true;
Thread.currentThread().interrupt();
throw new IllegalStateException(interruptedException);
}
+ } catch (ValidationException e) {
+ logger.error("Validation exception while running blank transaction indicates programming error", e);
+ throw new RuntimeException("Validation exception while running blank transaction indicates programming error", e);
}
}
- throw lastException;
+ throw new RuntimeException("Maximal number of attempts reached and still cannot get optimistic lock from " +
+ "config manager",lastException);
}
@Override
if(factory == null) {
throw new NullPointerException("ServiceReference of class" + serviceReference.getClass() + "not found.");
}
- StringBuffer errors = new StringBuffer();
+
String moduleName = factory.getImplementationName();
if (moduleName == null || moduleName.isEmpty()) {
throw new IllegalStateException(
throw new NullPointerException("Bundle context of " + factory + " ModuleFactory not found.");
}
logger.debug("Reading factory {} {}", moduleName, factory);
- String error = null;
+
Map.Entry<ModuleFactory, BundleContext> conflicting = result.get(moduleName);
if (conflicting != null) {
- error = String
- .format("Module name is not unique. Found two conflicting factories with same name '%s': " +
- "\n\t%s\n\t%s\n", moduleName, conflicting.getKey(), factory);
-
- }
-
- if (error == null) {
+ String error = String
+ .format("Module name is not unique. Found two conflicting factories with same name '%s': '%s' '%s'",
+ moduleName, conflicting.getKey(), factory);
+ logger.error(error);
+ throw new IllegalArgumentException(error);
+ } else {
result.put(moduleName, new AbstractMap.SimpleImmutableEntry<>(factory,
serviceReference.getBundle().getBundleContext()));
- } else {
- errors.append(error);
- }
- if (errors.length() > 0) {
- throw new IllegalArgumentException(errors.toString());
}
}
return result;
private RuntimeGeneratedMappingServiceActivator mappingServiceActivator;
@Override
- public void start(BundleContext context) throws Exception {
+ public void start(BundleContext context) {
// track bundles containing YangModuleInfo
ModuleInfoBundleTracker moduleInfoBundleTracker = new ModuleInfoBundleTracker();
try {
configRegistryJMXRegistrator.registerToJMX(configRegistry);
} catch (InstanceAlreadyExistsException e) {
- throw new RuntimeException("Config Registry was already registered to JMX", e);
+ throw new IllegalStateException("Config Registry was already registered to JMX", e);
}
ServiceTracker<ModuleFactory, Object> serviceTracker = new ServiceTracker<>(context, ModuleFactory.class,
}
@Override
- public void stop(BundleContext context) throws Exception {
+ public void stop(BundleContext context) {
try {
configRegistry.close();
} catch (Exception e) {
import org.slf4j.LoggerFactory;
import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Collection;
import java.util.LinkedList;
errorMessage = logMessage("Class {} does not implement {} in bundle {}", clazz, YangModelBindingProvider.class, bundle);
throw new IllegalStateException(errorMessage);
}
-
+ YangModelBindingProvider instance;
try {
- Object instance = clazz.newInstance();
- Object result = clazz.getMethod(GET_MODULE_INFO_METHOD).invoke(instance);
-
- if (YangModuleInfo.class.isAssignableFrom(result.getClass()) == false) {
- errorMessage = logMessage("Error invoking method not found {} in bundle {}, reason {}",
- GET_MODULE_INFO_METHOD, bundle, "Not assignable from " + YangModuleInfo.class);
- } else {
- return (YangModuleInfo) result;
- }
-
+ Object instanceObj = clazz.newInstance();
+ instance = YangModelBindingProvider.class.cast(instanceObj);
} catch (InstantiationException e) {
errorMessage = logMessage("Could not instantiate {} in bundle {}, reason {}", moduleInfoClass, bundle, e);
+ throw new IllegalStateException(errorMessage, e);
} catch (IllegalAccessException e) {
errorMessage = logMessage("Illegal access during instatiation of class {} in bundle {}, reason {}",
moduleInfoClass, bundle, e);
- } catch (NoSuchMethodException e) {
- errorMessage = logMessage("Method not found {} in bundle {}, reason {}", GET_MODULE_INFO_METHOD, bundle, e);
- } catch (InvocationTargetException e) {
- errorMessage = logMessage("Error invoking method {} in bundle {}, reason {}", GET_MODULE_INFO_METHOD,
- bundle, e);
+ throw new IllegalStateException(errorMessage, e);
}
+ try{
+ return instance.getModuleInfo();
+ } catch (NoClassDefFoundError e) {
- throw new IllegalStateException(errorMessage);
+
+ logger.error("Error while executing getModuleInfo on {}", instance, e);
+ throw e;
+ }
}
private static Class<?> loadClass(String moduleInfoClass, Bundle bundle) {
*/
package org.opendaylight.controller.config.manager.impl.dependencyresolver;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-
-import java.util.Arrays;
-import java.util.List;
-
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.api.JmxAttribute;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
public class DependencyResolverManagerTest {
public void setUp() {
transactionStatus = mock(TransactionStatus.class);
ServiceReferenceReadableRegistry mockedRegistry = mock(ServiceReferenceReadableRegistry.class);
- tested = new DependencyResolverManager("txName", transactionStatus, mockedRegistry, null);
+ tested = new DependencyResolverManager(new TransactionIdentifier("txName"), transactionStatus, mockedRegistry, null);
doNothing().when(transactionStatus).checkCommitStarted();
doNothing().when(transactionStatus).checkNotCommitted();
}
private static void mockGetInstance(DependencyResolverManager tested,
ModuleIdentifier moduleIdentifier) {
- ModuleInternalTransactionalInfo mock = mock(ModuleInternalTransactionalInfo.class);
- doReturn(moduleIdentifier).when(mock).getIdentifier();
- doReturn(mockedModule()).when(mock).getModule();
- tested.put(mock);
+
+ ModuleFactory moduleFactory = mock(ModuleFactory.class);
+ ModuleInternalInfo maybeOldInternalInfo = null;
+ TransactionModuleJMXRegistration transactionModuleJMXRegistration = null;
+ boolean isDefaultBean = false;
+
+ tested.put(moduleIdentifier,
+ mockedModule(),
+ moduleFactory,
+ maybeOldInternalInfo,
+ transactionModuleJMXRegistration,
+ isDefaultBean);
}
private static Module mockedModule() {
*/
package org.opendaylight.controller.config.manager.testingservices.parallelapsp;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.Closeable;
-
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.management.ObjectName;
-
+import com.google.common.base.Strings;
import org.opendaylight.controller.config.api.DependencyResolver;
import org.opendaylight.controller.config.api.JmxAttribute;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Strings;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.management.ObjectName;
+import java.io.Closeable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
/**
* Represents service that has dependency to thread pool.
import javax.management.MBeanException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
-import javax.management.RuntimeMBeanException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
private void testValidation(ConfigTransactionClient transaction)
throws InstanceAlreadyExistsException, ReflectionException,
- InstanceNotFoundException, MBeanException {
+ InstanceNotFoundException, MBeanException, ConflictingVersionException {
ObjectName fixed1names = transaction.createModule(
TestingFixedThreadPoolModuleFactory.NAME, fixed1);
// call validate on config bean
platformMBeanServer.invoke(fixed1names, "validate", new Object[0],
new String[0]);
fail();
- } catch (RuntimeMBeanException e) {
- RuntimeException targetException = e.getTargetException();
+ } catch (MBeanException e) {
+ Exception targetException = e.getTargetException();
assertNotNull(targetException);
assertEquals(ValidationException.class, targetException.getClass());
}
}
@Test
- public void testAbort() {
+ public void testAbort() throws InstanceAlreadyExistsException, ValidationException {
ConfigTransactionJMXClient transaction = configRegistryClient
.createTransaction();
assertEquals(1, configRegistryClient.getOpenConfigs().size());
transaction.createModule(TestingFixedThreadPoolModuleFactory.NAME,
fixed1);
fail();
- } catch (Exception e) {
- assertTrue(e.getCause() instanceof InstanceNotFoundException);
+ } catch (IllegalStateException e) {
+ assertEquals("Configuration was aborted", e.getMessage());
}
try {
transaction.validateConfig();
fail();
- } catch (Exception e) {
- assertTrue(e.getCause() instanceof InstanceNotFoundException);
+ } catch (IllegalStateException e) {
+ assertEquals("Configuration was aborted", e.getMessage());
}
assertEquals(0, configRegistryClient.getOpenConfigs().size());
}
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-subsystem</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>config-module-archetype</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-persister-api</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-persister-directory-adapter</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-persister-directory-autodetect-adapter</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-persister-directory-xml-adapter</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-persister-file-adapter</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-persister-file-xml-adapter</artifactId>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-subsystem</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>config-plugin-parent</artifactId>
<properties>
<jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
- <config.version>0.2.4-SNAPSHOT</config.version>
</properties>
<build>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>config-util</artifactId>
*/
package org.opendaylight.controller.config.util;
-import java.util.Map;
-import java.util.Set;
+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.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import javax.management.Attribute;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.JMX;
+import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
-import javax.management.RuntimeMBeanException;
-
-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.api.jmx.ConfigRegistryMXBean;
-import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
-import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import java.util.Map;
+import java.util.Set;
public class ConfigTransactionJMXClient implements ConfigTransactionClient {
private final ConfigRegistryMXBean configRegistryMXBeanProxy;
throws ValidationException {
try {
configMBeanServer.invoke(configBeanON, "validate", null, null);
+ } catch (MBeanException e) {
+ Exception targetException = e.getTargetException();
+ if (targetException instanceof ValidationException){
+ throw (ValidationException) targetException;
+ } else {
+ throw new RuntimeException(e);
+ }
} catch (JMException e) {
throw new RuntimeException(e);
- } catch (RuntimeMBeanException e) {
- throw e.getTargetException();
}
}
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<artifactId>logback-config</artifactId>
*/
package org.opendaylight.controller.config.yang.logback.config;
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InstanceNotFoundException;
-import javax.management.JMX;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
+import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
+import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
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.ObjectNameUtil;
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.slf4j.LoggerFactory;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
-import ch.qos.logback.classic.joran.JoranConfigurator;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.joran.spi.JoranException;
-import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
-import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMX;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
-import com.google.common.collect.Lists;
+import static org.junit.Assert.assertEquals;
public class LogbackModuleWithInitialConfigurationTest extends AbstractConfigTest {
}
public ObjectName createBeans() throws JoranException, InstanceAlreadyExistsException, IOException,
- MalformedObjectNameException, InstanceNotFoundException {
+ MalformedObjectNameException, InstanceNotFoundException, ValidationException, ConflictingVersionException {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
*/
package org.opendaylight.controller.config.yang.logback.config;
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.util.List;
-
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InstanceNotFoundException;
-import javax.management.JMX;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
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.manager.impl.AbstractConfigTest;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
import org.slf4j.LoggerFactory;
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.joran.JoranConfigurator;
-import ch.qos.logback.core.joran.spi.JoranException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMX;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.List;
-import com.google.common.collect.Lists;
+import static org.junit.Assert.assertEquals;
public class LogbackWithXmlConfigModuleTest extends AbstractConfigTest {
* @throws MalformedObjectNameException
*/
@Test
- public void test() throws InstanceAlreadyExistsException, InstanceNotFoundException, MalformedObjectNameException {
+ public void test() throws InstanceAlreadyExistsException, InstanceNotFoundException, MalformedObjectNameException, ValidationException, ConflictingVersionException {
ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
ObjectName nameRetrieved = transaction.lookupConfigBean(factory.getImplementationName(), LogbackModuleFactory.INSTANCE_NAME);
*/
@Test
public void testAddNewLogger() throws InstanceAlreadyExistsException, InstanceNotFoundException,
- MalformedObjectNameException {
+ MalformedObjectNameException, ValidationException, ConflictingVersionException {
ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
ObjectName nameRetrieved = transaction.lookupConfigBean(factory.getImplementationName(), LogbackModuleFactory.INSTANCE_NAME);
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
--- /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.netty.eventexecutor;
+
+import com.google.common.reflect.AbstractInvocationHandler;
+import com.google.common.reflect.Reflection;
+import io.netty.util.concurrent.EventExecutor;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+public interface AutoCloseableEventExecutor extends EventExecutor, AutoCloseable {
+
+
+ public static class CloseableEventExecutorMixin implements AutoCloseable {
+ public static final int DEFAULT_SHUTDOWN_SECONDS = 1;
+ private final EventExecutor eventExecutor;
+
+ public CloseableEventExecutorMixin(EventExecutor eventExecutor) {
+ this.eventExecutor = eventExecutor;
+ }
+
+ @Override
+ public void close() {
+ eventExecutor.shutdownGracefully(0, DEFAULT_SHUTDOWN_SECONDS, TimeUnit.SECONDS);
+ }
+
+
+ public static AutoCloseable createCloseableProxy(final EventExecutor eventExecutor) {
+ final CloseableEventExecutorMixin closeableGlobalEventExecutorMixin =
+ new CloseableEventExecutorMixin(eventExecutor);
+ return Reflection.newProxy(AutoCloseableEventExecutor.class, new AbstractInvocationHandler() {
+ @Override
+ protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("close")) {
+ closeableGlobalEventExecutorMixin.close();
+ return null;
+ } else {
+ return method.invoke(eventExecutor, args);
+ }
+ }
+ });
+ }
+
+
+ }
+}
\ No newline at end of file
*/
package org.opendaylight.controller.config.yang.netty.eventexecutor;
-import com.google.common.reflect.AbstractInvocationHandler;
-import com.google.common.reflect.Reflection;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.GlobalEventExecutor;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.AutoCloseableEventExecutor.CloseableEventExecutorMixin;
public final class GlobalEventExecutorModule extends
org.opendaylight.controller.config.yang.netty.eventexecutor.AbstractGlobalEventExecutorModule {
@Override
public java.lang.AutoCloseable createInstance() {
- final CloseableGlobalEventExecutorMixin closeableGlobalEventExecutorMixin =
- new CloseableGlobalEventExecutorMixin(GlobalEventExecutor.INSTANCE);
- return Reflection.newProxy(AutoCloseableEventExecutor.class, new AbstractInvocationHandler() {
- @Override
- protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
- if (method.getName().equals("close")) {
- closeableGlobalEventExecutorMixin.close();
- return null;
- } else {
- return method.invoke(GlobalEventExecutor.INSTANCE, args);
- }
- }
- });
+ EventExecutor eventExecutor = GlobalEventExecutor.INSTANCE;
+ return CloseableEventExecutorMixin.createCloseableProxy(eventExecutor);
}
- public static interface AutoCloseableEventExecutor extends EventExecutor, AutoCloseable {
-
- }
- public static class CloseableGlobalEventExecutorMixin implements AutoCloseable {
- private final GlobalEventExecutor eventExecutor;
- public CloseableGlobalEventExecutorMixin(GlobalEventExecutor eventExecutor) {
- this.eventExecutor = eventExecutor;
- }
-
- @Override
- public void close() {
- eventExecutor.shutdownGracefully(0, 1, TimeUnit.SECONDS);
- }
- }
}
--- /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.netty.eventexecutor;
+
+import io.netty.util.concurrent.EventExecutor;
+import io.netty.util.concurrent.ImmediateEventExecutor;
+import org.opendaylight.controller.config.yang.netty.eventexecutor.AutoCloseableEventExecutor.CloseableEventExecutorMixin;
+
+public final class ImmediateEventExecutorModule extends org.opendaylight.controller.config.yang.netty.eventexecutor.AbstractImmediateEventExecutorModule {
+
+ public ImmediateEventExecutorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ImmediateEventExecutorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ ImmediateEventExecutorModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {
+ // Add custom validation for module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ EventExecutor eventExecutor = ImmediateEventExecutor.INSTANCE;
+ return CloseableEventExecutorMixin.createCloseableProxy(eventExecutor);
+ }
+}
--- /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.netty.eventexecutor;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.osgi.framework.BundleContext;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class ImmediateEventExecutorModuleFactory extends org.opendaylight.controller.config.yang.netty.eventexecutor.AbstractImmediateEventExecutorModuleFactory {
+ public static final String SINGLETON_NAME = "singleton";
+
+ @Override
+ public ImmediateEventExecutorModule instantiateModule(String instanceName, DependencyResolver dependencyResolver, ImmediateEventExecutorModule oldModule, AutoCloseable oldInstance, BundleContext bundleContext) {
+ checkArgument(SINGLETON_NAME.equals(instanceName), "Illegal instance name '" + instanceName + "', only allowed name is " + SINGLETON_NAME);
+ return super.instantiateModule(instanceName, dependencyResolver, oldModule, oldInstance, bundleContext);
+ }
+
+ @Override
+ public ImmediateEventExecutorModule instantiateModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
+ checkArgument(SINGLETON_NAME.equals(instanceName), "Illegal instance name '" + instanceName + "', only allowed name is " + SINGLETON_NAME);
+ return super.instantiateModule(instanceName, dependencyResolver, bundleContext);
+ }
+}
augment "/config:modules/config:module/config:configuration" {
case netty-global-event-executor {
when "/config:modules/config:module/config:type = 'netty-global-event-executor'";
+ }
+ }
+ identity netty-immediate-event-executor {
+ base config:module-type;
+ config:provided-service netty:netty-event-executor;
+ config:java-name-prefix ImmediateEventExecutor;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netty-immediate-event-executor {
+ when "/config:modules/config:module/config:type = 'netty-immediate-event-executor'";
}
}
}
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
</parent>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<artifactId>config-subsystem</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<jacoco.version>0.6.2.201302030002</jacoco.version>
<slf4j.version>1.7.2</slf4j.version>
<salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
- <config.version>0.2.4-SNAPSHOT</config.version>
</properties>
<dependencies>
<parent>
<artifactId>config-plugin-parent</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>config-plugin-parent</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
shutdownViaRuntimeJMX(secret);
}
- private void setSecret(String secret) throws InstanceNotFoundException {
+ private void setSecret(String secret) throws InstanceNotFoundException, ValidationException, ConflictingVersionException {
ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
ObjectName on = transaction.lookupConfigBean(NAME, NAME);
ShutdownModuleMXBean proxy = transaction.newMXBeanProxy(on, ShutdownModuleMXBean.class);
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>yang-jmx-generator-it</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>yang-jmx-generator-plugin</artifactId>
ftlFile.getFtlTempleteLocation());
try {
template.process(ftlFile, writer);
- } catch (Throwable e) {
+ } catch (Exception e) {
throw new IllegalStateException(
"Template error while generating " + ftlFile, e);
}
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>yang-jmx-generator</artifactId>
boolean providedServiceWasSet = false;
for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
// TODO: test this
- if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
- // no op: 0 or more provided identities are allowed
- } else if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
+ boolean unknownNodeIsProvidedServiceExtension = ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType());
+ // true => no op: 0 or more provided identities are allowed
+
+ if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
// 0..1 allowed
checkState(
providedServiceWasSet == false,
format("More than one language extension %s is not allowed here: %s",
ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME, id));
providedServiceWasSet = true;
- } else {
+ } else if (unknownNodeIsProvidedServiceExtension == false) {
throw new IllegalStateException("Unexpected language extension " + unknownNode.getNodeType());
}
}
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@Override
public CompositeType getOpenType() {
- String description = getNullableDescription() == null ? getAttributeYangName()
- : getNullableDescription();
- final String[] itemNames = new String[yangNameToAttributeMap.keySet()
- .size()];
- String[] itemDescriptions = itemNames;
- FunctionImpl functionImpl = new FunctionImpl(itemNames);
+ String description = getNullableDescription() == null ? getAttributeYangName() : getNullableDescription();
+
+ FunctionImpl functionImpl = new FunctionImpl();
Map<String, AttributeIfc> jmxPropertiesToTypesMap = getJmxPropertiesToTypesMap();
OpenType<?>[] itemTypes = Collections2.transform(
jmxPropertiesToTypesMap.entrySet(), functionImpl).toArray(
new OpenType<?>[] {});
+ String[] itemNames = functionImpl.getItemNames();
try {
// TODO add package name to create fully qualified name for this
// type
CompositeType compositeType = new CompositeType(
getUpperCaseCammelCase(), description, itemNames,
- itemDescriptions, itemTypes);
+ itemNames, itemTypes);
return compositeType;
} catch (OpenDataException e) {
throw new RuntimeException("Unable to create CompositeType for "
return packageName;
}
- private static final class FunctionImpl implements
- Function<Entry<String, AttributeIfc>, OpenType<?>> {
- private final String[] itemNames;
- int i = 0;
+}
- private FunctionImpl(String[] itemNames) {
- this.itemNames = itemNames;
- }
+class FunctionImpl implements
+ Function<Entry<String, AttributeIfc>, OpenType<?>> {
+ private final List<String> itemNames = new ArrayList<>();
- @Override
- public OpenType<?> apply(Entry<String, AttributeIfc> input) {
- AttributeIfc innerType = input.getValue();
- itemNames[i++] = input.getKey();
- return innerType.getOpenType();
- }
+ @Override
+ public OpenType<?> apply(Entry<String, AttributeIfc> input) {
+ AttributeIfc innerType = input.getValue();
+ itemNames.add(input.getKey());
+ return innerType.getOpenType();
+ }
+
+ public String[] getItemNames(){
+ return itemNames.toArray(new String[itemNames.size()]);
}
}
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>yang-store-api</artifactId>
<parent>
<artifactId>config-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>yang-store-impl</artifactId>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-plugin-parent</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../config-plugin-parent</relativePath>
</parent>
@Override
public java.lang.AutoCloseable createInstance() {
- System.err.println(getAfi());
- System.err.println(getAfiIdentity());
+ org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
+ logger.info("Afi: {}", getAfi());
+ logger.info("Afi class: {}", getAfiIdentity());
- getAfiIdentity();
for (Identities identities : getIdentities()) {
- identities.resolveAfi();
- identities.resolveSafi();
+ logger.info("Identities Afi class: {}", identities.resolveAfi());
+ logger.info("Identities Safi class: {}", identities.resolveSafi());
+
}
- getIdentitiesContainer().resolveAfi();
+ logger.info("IdentityContainer Afi class: {}", getIdentitiesContainer().resolveAfi());
return new AutoCloseable() {
@Override
- System.err.println(getAfi());
- System.err.println(getAfiIdentity());
+ org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
+ logger.info("Afi: {}", getAfi());
+ logger.info("Afi class: {}", getAfiIdentity());
- getAfiIdentity();
for (Identities identities : getIdentities()) {
- identities.resolveAfi();
- identities.resolveSafi();
+ logger.info("Identities Afi class: {}", identities.resolveAfi());
+ logger.info("Identities Safi class: {}", identities.resolveSafi());
+
}
- getIdentitiesContainer().resolveAfi();
+ logger.info("IdentityContainer Afi class: {}", getIdentitiesContainer().resolveAfi());
return new AutoCloseable() {
@Override
fi
function usage {
- echo "Usage: $0 [-jmx] [-jmxport <num>] [-debug] [-debugsuspend] [-debugport <num>] [-start [<console port>]] [-stop] [-status] [-console] [-help] [<other args will automatically be used for the JVM>]"
+ echo "Usage: $0 [-jmx] [-jmxport <num>] [-debug] [-debugsuspend] [-debugport <num>] [-start [<console port>]] [-stop] [-status] [-console] [-help] [-agentpath:<path to lib>] [<other args will automatically be used for the JVM>]"
exit 1
}
consolestart=1
dohelp=0
extraJVMOpts=""
+agentPath=""
unknown_option=0
while true ; do
case "$1" in
-help) dohelp=1; shift;;
-D*) extraJVMOpts="${extraJVMOpts} $1"; shift;;
-X*) extraJVMOpts="${extraJVMOpts} $1"; shift;;
+ -agentpath:*) agentPath="$1"; shift;;
"") break ;;
*) echo "Unknown option $1"; unknown_option=1; shift ;;
esac
exit -1
fi
$JAVA_HOME/bin/java ${extraJVMOpts} \
+ ${agentPath} \
-Djava.io.tmpdir="${iotmpdir}/work/tmp" \
-Dosgi.install.area="${bdir}" \
-Dosgi.configuration.area="${confarea}/configuration" \
exit -1
fi
$JAVA_HOME/bin/java ${extraJVMOpts} \
+ ${agentPath} \
-Djava.io.tmpdir="${iotmpdir}/work/tmp" \
-Dosgi.install.area="${bdir}" \
-Dosgi.configuration.area="${confarea}/configuration" \
List<FlowEntryInstall> list = new ArrayList<FlowEntryInstall>(groupFlows.get(groupName));
toBeRemoved = list.size();
for (FlowEntryInstall entry : list) {
- Status status = this.removeEntry(entry.getOriginal(), false);
+ // since this is the entry that was stored in groupFlows
+ // it is already validated and merged
+ // so can call removeEntryInternal directly
+ Status status = this.removeEntryInternal(entry, false);
if (status.isSuccess()) {
toBeRemoved -= 1;
} else {
package org.opendaylight.controller.sal.compatibility.adsal;
import java.math.BigInteger;
-import java.util.concurrent.Future;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.Futures;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.compatibility.InventoryMapping;
import org.opendaylight.controller.sal.compatibility.ToSalConversionsUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
public class FlowServiceAdapter implements SalFlowService, IFlowProgrammerListener {
private static final Logger LOG = LoggerFactory.getLogger(FlowServiceAdapter.class);
}
@Override
- public Future<RpcResult<AddFlowOutput>> addFlow(AddFlowInput input) {
+ public ListenableFuture<RpcResult<AddFlowOutput>> addFlow(AddFlowInput input) {
Flow flow = ToSalConversionsUtils.toFlow(input, null);
@SuppressWarnings("unchecked")
}
@Override
- public Future<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput input) {
+ public ListenableFuture<RpcResult<RemoveFlowOutput>> removeFlow(RemoveFlowInput input) {
Flow flow = ToSalConversionsUtils.toFlow(input, null);
@SuppressWarnings("unchecked")
}
@Override
- public Future<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput input) {
+ public ListenableFuture<RpcResult<UpdateFlowOutput>> updateFlow(UpdateFlowInput input) {
@SuppressWarnings("unchecked")
org.opendaylight.controller.sal.core.Node node = InventoryMapping.toAdNode((InstanceIdentifier<Node>) input
.getNode().getValue());
import java.util.concurrent.Future;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.Futures;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.compatibility.FromSalConversionsUtils;
import org.opendaylight.controller.sal.compatibility.InventoryMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
public class FlowStatisticsAdapter implements OpendaylightFlowStatisticsService, IReadServiceListener{
private static final Logger LOG = LoggerFactory.getLogger(FlowStatisticsAdapter.class);
@Override
public Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput>> getAggregateFlowStatisticsFromFlowTableForAllFlows(
GetAggregateFlowStatisticsFromFlowTableForAllFlowsInput input) {
- //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
+ //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
// generating aggregate flow statistics out of those individual flow stats.
return null;
}
@Override
public Future<RpcResult<GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutput>> getAggregateFlowStatisticsFromFlowTableForGivenMatch(
GetAggregateFlowStatisticsFromFlowTableForGivenMatchInput input) {
- //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
+ //TODO: No supported API exist in AD-SAL, it can either be implemented by fetching all the stats of the flows and
// generating aggregate flow statistics out of those individual flow stats.
return null;
}
@Override
- public Future<RpcResult<GetAllFlowStatisticsFromFlowTableOutput>> getAllFlowStatisticsFromFlowTable(
+ public ListenableFuture<RpcResult<GetAllFlowStatisticsFromFlowTableOutput>> getAllFlowStatisticsFromFlowTable(
GetAllFlowStatisticsFromFlowTableInput input) {
GetAllFlowStatisticsFromFlowTableOutput rpcResultType = null;
boolean rpcResultBool = false;
GetAllFlowStatisticsFromFlowTableOutputBuilder builder = new GetAllFlowStatisticsFromFlowTableOutputBuilder();
builder.setTransactionId(new TransactionId(new BigInteger("0")));
rpcResultType = builder.setFlowAndStatisticsMapList(flowsStatistics).build();
-
+
rpcResultBool = true;
} catch (ConstructionException e) {
LOG.error(e.getMessage());
* Essentially this API will return the same result as getAllFlowStatisticsFromFlowTable
*/
@Override
- public Future<RpcResult<GetAllFlowsStatisticsFromAllFlowTablesOutput>> getAllFlowsStatisticsFromAllFlowTables(
+ public ListenableFuture<RpcResult<GetAllFlowsStatisticsFromAllFlowTablesOutput>> getAllFlowsStatisticsFromAllFlowTables(
GetAllFlowsStatisticsFromAllFlowTablesInput input) {
-
+
GetAllFlowsStatisticsFromAllFlowTablesOutput rpcResultType = null;
boolean rpcResultBool = false;
GetAllFlowsStatisticsFromAllFlowTablesOutputBuilder builder = new GetAllFlowsStatisticsFromAllFlowTablesOutputBuilder();
builder.setTransactionId(new TransactionId(new BigInteger("0")));
rpcResultType = builder.setFlowAndStatisticsMapList(flowsStatistics).build();
-
+
rpcResultBool = true;
} catch (ConstructionException e) {
LOG.error(e.getMessage());
}
@Override
- public Future<RpcResult<GetFlowStatisticsFromFlowTableOutput>> getFlowStatisticsFromFlowTable(
+ public ListenableFuture<RpcResult<GetFlowStatisticsFromFlowTableOutput>> getFlowStatisticsFromFlowTable(
GetFlowStatisticsFromFlowTableInput input) {
GetFlowStatisticsFromFlowTableOutput rpcResultType = null;
boolean rpcResultBool = false;
public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
NodeConnectorStatisticsUpdateBuilder nodeConnectorStatisticsUpdateBuilder = new NodeConnectorStatisticsUpdateBuilder();
List<NodeConnectorStatisticsAndPortNumberMap> nodeConnectorStatistics = toOdNodeConnectorStatistics(ncStatsList);
-
+
nodeConnectorStatisticsUpdateBuilder.setNodeConnectorStatisticsAndPortNumberMap(nodeConnectorStatistics);
nodeConnectorStatisticsUpdateBuilder.setMoreReplies(false);
nodeConnectorStatisticsUpdateBuilder.setTransactionId(null);
@Override
public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
-
- FlowTableStatisticsUpdateBuilder flowTableStatisticsUpdateBuilder = new FlowTableStatisticsUpdateBuilder();
-
+
+ FlowTableStatisticsUpdateBuilder flowTableStatisticsUpdateBuilder = new FlowTableStatisticsUpdateBuilder();
+
List<FlowTableAndStatisticsMap> flowTableStatistics = toOdFlowTableStatistics(tableStatsList);
flowTableStatisticsUpdateBuilder.setFlowTableAndStatisticsMap(flowTableStatistics);
flowTableStatisticsUpdateBuilder.setMoreReplies(false);
@Override
public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
// TODO which *StatisticsUpdated interface should be used?
-
+
}
private List<FlowAndStatisticsMapList> toOdFlowsStatistics(List<FlowOnNode> flowsOnNode) {
}
private List<FlowTableAndStatisticsMap> toOdFlowTableStatistics(List<NodeTableStatistics> tableStatsList) {
-
+
List<FlowTableAndStatisticsMap> flowTableStatsMap = new ArrayList<FlowTableAndStatisticsMap>();
for (NodeTableStatistics nodeTableStatistics : tableStatsList) {
FlowTableAndStatisticsMapBuilder flowTableAndStatisticsMapBuilder = new FlowTableAndStatisticsMapBuilder();
flowTableAndStatisticsMapBuilder.setTableId(new TableId((short)nodeTableStatistics.getNodeTable().getID()));
flowTableStatsMap.add(flowTableAndStatisticsMapBuilder.build());
}
-
+
return flowTableStatsMap;
}
List<NodeConnectorStatisticsAndPortNumberMap> nodeConnectorStatisticsList = new ArrayList<NodeConnectorStatisticsAndPortNumberMap>();
for(NodeConnectorStatistics ofNodeConnectorStatistics : ncStatsList){
NodeConnectorStatisticsAndPortNumberMapBuilder nodeConnectorStatisticsAndPortNumberMapBuilder = new NodeConnectorStatisticsAndPortNumberMapBuilder();
-
+
nodeConnectorStatisticsAndPortNumberMapBuilder.setBytes(extractBytes(ofNodeConnectorStatistics));
nodeConnectorStatisticsAndPortNumberMapBuilder.setCollisionCount(toBI(ofNodeConnectorStatistics.getCollisionCount()));
nodeConnectorStatisticsAndPortNumberMapBuilder.setDuration(null);
nodeConnectorStatisticsAndPortNumberMapBuilder.setTransmitErrors(toBI(ofNodeConnectorStatistics.getTransmitErrorCount()));
nodeConnectorStatisticsList.add(nodeConnectorStatisticsAndPortNumberMapBuilder.build());
}
-
+
return nodeConnectorStatisticsList;
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<!-- Dependency Versions -->
<mockito.version>1.9.5</mockito.version>
- <xtend.version>2.4.3</xtend.version>
+
<!-- Sonar properties using jacoco to retrieve integration test results -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.branch>${user.name}-private-view</sonar.branch>
<sonar.language>java</sonar.language>
<exam.version>3.0.0</exam.version>
- <sal.version>0.7.1-SNAPSHOT</sal.version>
+ <sal.version>0.8.1-SNAPSHOT</sal.version>
</properties>
<dependencyManagement>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
- <dependency>
- <groupId>org.eclipse.xtend</groupId>
- <artifactId>org.eclipse.xtend.lib</artifactId>
- <version>${xtend.version}</version>
- </dependency>
+
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
*/
package org.opendaylight.controller.sal.connector.remoterpc.api;
-import java.util.Map;
import java.util.Set;
public interface RoutingTable<I,R> {
-
-
/**
- * Adds a network address for the route. If address for route
- * exists, appends the address to the list
+ * Adds a network address for the route. If the route already exists,
+ * it throws <code>DuplicateRouteException</code>.
+ * This method would be used when registering a global service.
+ *
*
* @param routeId route identifier
* @param route network address
- * @throws RoutingTableException for any logical exception
+ * @throws DuplicateRouteException
+ * @throws RoutingTableException
+ */
+ public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException;
+
+ /**
+ * Remove the route.
+ * This method would be used when registering a global service.
+ * @param routeId
+ * @throws RoutingTableException
* @throws SystemException
*/
- public void addRoute(I routeId, R route) throws RoutingTableException,SystemException;
+ public void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException;
- /**
+ /**
* Adds a network address for the route. If the route already exists,
* it throws <code>DuplicateRouteException</code>.
* This method would be used when registering a global service.
* @throws DuplicateRouteException
* @throws RoutingTableException
*/
- public void addGlobalRoute(I routeId, R route) throws RoutingTableException, SystemException;
-
+ public R getGlobalRoute(I routeId) throws RoutingTableException, SystemException;
+ /**
+ * 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) throws RoutingTableException,SystemException;
/**
* @param routeId
* @param route
*/
- public void removeRoute(I routeId, R route);
+ public void removeRoute(I routeId, R route) throws RoutingTableException,SystemException;
+ /**
+ * Adds address for a set of route identifiers. If address for route
+ * exists, appends the address to the set.
+ *
+ * @param routeIds a set of routeIds
+ * @param route network address
+ * @throws RoutingTableException for any logical exception
+ * @throws SystemException
+ */
+ public void addRoutes(Set<I> routeIds, R route) throws RoutingTableException,SystemException;
- /**
- * 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;
+ /**
+ * Removes address for a set of route identifiers.
+ *
+ * @param routeIds a set of routeIds
+ * @param route network address
+ * @throws RoutingTableException for any logical exception
+ * @throws SystemException
+ */
+ public void removeRoutes(Set<I> routeIds, R route) throws RoutingTableException,SystemException;
/**
* Returns a set of network addresses associated with this route
*/
public Set<R> getRoutes(I routeId);
- /**
- * Returns all network addresses stored in the table
- * @return
- */
- public Set<Map.Entry> getAllRoutes();
/**
- * 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
+ * Returns the last inserted address from the list of network addresses
+ * associated with the 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 R getLastAddedRoute(I routeId);
public class DuplicateRouteException extends RoutingTableException {
public DuplicateRouteException(String message) {
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);
+ propSet.add(RoutingTableImpl.GLOBALRPC_CACHE);
+ propSet.add(RoutingTableImpl.RPC_CACHE);
props.put(CACHE_UPDATE_AWARE_REGISTRY_KEY, propSet);
c.setInterface(new String[] { RoutingTable.class.getName(),ICacheUpdateAware.class.getName() }, props);
package org.opendaylight.controller.sal.connector.remoterpc.impl;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
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.clustering.services.*;
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 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.Map;
-import java.util.Set;
+import java.util.*;
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 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>());
+ private IClusterGlobalServices clusterGlobalServices = null;
- public RoutingTableImpl() {
- }
+ private ConcurrentMap<I,R> globalRpcCache = null;
+ private ConcurrentMap<I, LinkedHashSet<R>> rpcCache = null; //need routes to ordered by insert-order
- @Override
- public void addRoute(I routeId, R route) throws RoutingTableException {
- throw new UnsupportedOperationException(" Not implemented yet!");
- }
+ public static final String GLOBALRPC_CACHE = "remoterpc_routingtable.globalrpc_cache";
+ public static final String RPC_CACHE = "remoterpc_routingtable.rpc_cache";
- @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();
-
- routingTableCache.put(routeId, route);
- clusterGlobalServices.tcommit();
- } else {
- throw new DuplicateRouteException(" There is already existing route " + existingRoute);
- }
-
- } catch (NotSupportedException|HeuristicRollbackException|RollbackException|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);
- }
+ public RoutingTableImpl() {
+ }
- }
+ @Override
+ public R getGlobalRoute(I routeId) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "getGlobalRoute: routeId cannot be null!");
+ return globalRpcCache.get(routeId);
+ }
- @Override
- public void removeRoute(I routeId, R route) {
- 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 {
+
+ log.debug("addGlobalRoute: adding a new route with id[{}] and value [{}]", routeId, route);
+ clusterGlobalServices.tbegin();
+ if (globalRpcCache.putIfAbsent(routeId, route) != null) {
+ throw new DuplicateRouteException(" There is already existing route " + routeId);
+ }
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | 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 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|HeuristicRollbackException|RollbackException|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 void removeGlobalRoute(I routeId) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
+ try {
+ log.debug("removeGlobalRoute: removing a new route with id [{}]", routeId);
+
+ clusterGlobalServices.tbegin();
+ globalRpcCache.remove(routeId);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | 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) {
+ @Override
+ public Set<R> getRoutes(I routeId) {
+ Preconditions.checkNotNull(routeId, "getRoutes: routeId cannot be null!");
+ Set<R> routes = rpcCache.get(routeId);
- // Note: currently works for global routes only wherein there is just single
- // route
- Preconditions.checkNotNull(routeId, "getARoute: routeId cannot be null!");
- R route = (R)routingTableCache.get(routeId);
- Set<R>routes = null;
- if(route !=null){
- routes = new HashSet<R>();
- routes.add(route);
- }
+ if (routes == null) return Collections.emptySet();
+
+ return ImmutableSet.copyOf(routes);
+ }
- return routes;
- }
+
+
+ public R getLastAddedRoute(I routeId) {
+
+ Set<R> routes = getRoutes(routeId);
+
+ if (routes.isEmpty()) return null;
+
+ R route = null;
+ Iterator<R> iter = routes.iterator();
+ while (iter.hasNext())
+ route = iter.next();
+
+ return route;
+ }
@Override
- public Set<Map.Entry> getAllRoutes() {
- return routingTableCache.entrySet();
+ public void addRoute(I routeId, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "addRoute: routeId cannot be null");
+ Preconditions.checkNotNull(route, "addRoute: route cannot be null");
+
+ try{
+ clusterGlobalServices.tbegin();
+ log.debug("addRoute: adding a route with k/v [{}/{}]", routeId, route);
+ threadSafeAdd(routeId, route);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | 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 R getARoute(I routeId) {
- throw new UnsupportedOperationException("Not implemented yet!");
+ public void addRoutes(Set<I> routeIds, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeIds, "addRoutes: routeIds must not be null");
+ for (I routeId : routeIds){
+ addRoute(routeId, route);
}
+ }
- /**
- * @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) {
+ @Override
+ public void removeRoute(I routeId, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeId, "removeRoute: routeId cannot be null!");
+ Preconditions.checkNotNull(route, "removeRoute: route cannot be null!");
- }
+ LinkedHashSet<R> routes = rpcCache.get(routeId);
+ if (routes == null) return;
- public void setRouteChangeListener(RouteChangeListener rcl) {
- if(rcl != null){
- routeChangeListeners.add(rcl);
- }else{
- log.warn("setRouteChangeListener called with null listener");
- }
- }
+ try {
+ log.debug("removeRoute: removing a new route with k/v [{}/{}]", routeId, route);
- public void unSetRouteChangeListener(RouteChangeListener rcl) {
- if(rcl != null){
- routeChangeListeners.remove(rcl);
- }else{
- log.warn("unSetRouteChangeListener called with null listener");
- }
+ clusterGlobalServices.tbegin();
+ threadSafeRemove(routeId, route);
+ clusterGlobalServices.tcommit();
+
+ } catch (NotSupportedException | HeuristicRollbackException | RollbackException | 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);
}
+ }
- /**
- * 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;
+ @Override
+ public void removeRoutes(Set<I> routeIds, R route) throws RoutingTableException, SystemException {
+ Preconditions.checkNotNull(routeIds, "removeRoutes: routeIds must not be null");
+ for (I routeId : routeIds){
+ removeRoute(routeId, route);
}
+ }
- public void setClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
- this.clusterGlobalServices = clusterGlobalServices;
+ /**
+ * This method guarantees that no 2 thread over write each other's changes.
+ * Just so that we dont end up in infinite loop, it tries for 100 times then throw
+ */
+ private void threadSafeAdd(I routeId, R route) {
+
+ for (int i=0;i<100;i++){
+
+ LinkedHashSet<R> updatedRoutes = new LinkedHashSet<>();
+ updatedRoutes.add(route);
+ LinkedHashSet<R> oldRoutes = rpcCache.putIfAbsent(routeId, updatedRoutes);
+ if (oldRoutes == null) return;
+
+ updatedRoutes = new LinkedHashSet<>(oldRoutes);
+ updatedRoutes.add(route);
+
+ if (rpcCache.replace(routeId, oldRoutes, updatedRoutes)) return;
}
+ //the method did not already return means it failed to add route in 10 attempts
+ throw new IllegalStateException("Failed to add route [" + routeId + "]");
+ }
+
+ /**
+ * This method guarantees that no 2 thread over write each other's changes.
+ * Just so that we dont end up in infinite loop, it tries for 10 times then throw
+ */
+ private void threadSafeRemove(I routeId, R route) {
+ LinkedHashSet<R> updatedRoutes = null;
+ for (int i=0;i<10;i++){
+ LinkedHashSet<R> oldRoutes = rpcCache.get(routeId);
+
+ // if route to be deleted is the only entry in the set then remove routeId from the cache
+ if ((oldRoutes.size() == 1) && oldRoutes.contains(route)){
+ rpcCache.remove(routeId);
+ return;
+ }
+
+ // if there are multiple routes for this routeId, remove the route to be deleted only from the set.
+ updatedRoutes = new LinkedHashSet<>(oldRoutes);
+ updatedRoutes.remove(route);
+ if (rpcCache.replace(routeId, oldRoutes, updatedRoutes)) return;
- public void unsetClusterGlobalServices(IClusterGlobalServices clusterGlobalServices) {
- if((clusterGlobalServices != null ) && (this.clusterGlobalServices.equals(clusterGlobalServices))){
- this.clusterGlobalServices = null;
- }
}
+ //the method did not already return means it failed to remove route in 10 attempts
+ throw new IllegalStateException("Failed to remove route [" + routeId + "]");
+ }
+
- /**
- * 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);
- }
+// /**
+// * @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;
+ }
+ }
+ /**
+ * Finds OR Creates clustered cache for Global RPCs
+ *
+ * @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 findOrCreateGlobalRpcCache() 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(GLOBALRPC_CACHE)) {
+
+ globalRpcCache = (ConcurrentMap<I,R>) clusterGlobalServices.createCache(GLOBALRPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+ log.debug("Cache created [{}] ", GLOBALRPC_CACHE);
+
+ } else {
+ globalRpcCache = (ConcurrentMap<I,R>) clusterGlobalServices.getCache(GLOBALRPC_CACHE);
+ log.debug("Cache exists [{}] ", GLOBALRPC_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");
- }
+ /**
+ * Finds OR Creates clustered cache for Routed RPCs
+ *
+ * @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 findOrCreateRpcCache() throws CacheExistException, CacheConfigException,
+ CacheListenerAddException {
+ // TBD: HOW DO WE DECIDE ON PROPERTIES OF THE CACHE i.e. what duration it
+ // should be caching?
+
+ if (clusterGlobalServices.existCache(RPC_CACHE)){
+ rpcCache = (ConcurrentMap<I,LinkedHashSet<R>>) clusterGlobalServices.getCache(RPC_CACHE);
+ log.debug("Cache exists [{}] ", RPC_CACHE);
+ return;
}
- /**
- * Get routing table method is useful for unit testing <note>It has package
- * scope</note>
- */
- ConcurrentMap getRoutingTableCache() {
- return this.routingTableCache;
+ //cache doesnt exist, create one
+ rpcCache = (ConcurrentMap<I,LinkedHashSet<R>>) clusterGlobalServices.createCache(RPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
+ log.debug("Cache created [{}] ", RPC_CACHE);
+ }
+
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ */
+ void init(Component c) {
+ try {
+
+ findOrCreateGlobalRpcCache();
+ findOrCreateRpcCache();
+
+ } catch (CacheExistException|CacheConfigException|CacheListenerAddException e) {
+ throw new IllegalStateException("could not construct routing table cache");
}
+ }
+
+ /**
+ * Useful for unit testing <note>It has package
+ * scope</note>
+ */
+ ConcurrentMap getGlobalRpcCache() {
+ return this.globalRpcCache;
+ }
+
+ /**
+ * Useful for unit testing <note>It has package
+ * scope</note>
+ */
+ ConcurrentMap getRpcCache() {
+ return this.rpcCache;
+ }
- /**
- * This is used from integration test NP rest API to check out the result of the
- * cache population
- * <Note> For testing purpose only-- use it wisely</Note>
- * @return
- */
- public String dumpRoutingTableCache(){
- Set<Map.Entry<I, R>> cacheEntrySet = this.routingTableCache.entrySet();
- StringBuilder sb = new StringBuilder();
- for(Map.Entry<I,R> entry:cacheEntrySet){
- sb.append("Key:").append(entry.getKey()).append("---->Value:")
- .append((entry.getValue() != null)?entry.getValue():"null")
- .append("\n");
- }
- return sb.toString();
+ /**
+ * This is used from integration test NP rest API to check out the result of the
+ * cache population
+ * <Note> For testing purpose only-- use it wisely</Note>
+ *
+ * @return
+ */
+ public String dumpGlobalRpcCache() {
+ Set<Map.Entry<I, R>> cacheEntrySet = this.globalRpcCache.entrySet();
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<I, R> entry : cacheEntrySet) {
+ sb.append("Key:").append(entry.getKey()).append("---->Value:")
+ .append((entry.getValue() != null) ? entry.getValue() : "null")
+ .append("\n");
}
+ return sb.toString();
+ }
- /**
- * 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);
- }
+ public String dumpRpcCache() {
+ Set<Map.Entry<I, LinkedHashSet<R>>> cacheEntrySet = this.rpcCache.entrySet();
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<I, LinkedHashSet<R>> entry : cacheEntrySet) {
+ sb.append("Key:").append(entry.getKey()).append("---->Value:")
+ .append((entry.getValue() != null) ? entry.getValue() : "null")
+ .append("\n");
}
+ return sb.toString();
+ }
+ /**
+ * 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 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);
- }
- }
+ /**
+ * 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
import junit.framework.Assert;
import org.apache.felix.dm.Component;
+import org.junit.After;
+import org.junit.Before;
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.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.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.Iterator;
+import java.util.LinkedHashSet;
import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
-/**
- * @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");
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace,"global");
+ private IClusterGlobalServices clusterService;
+ private RoutingTableImpl<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String> routingTable;
+ ConcurrentMap mockGlobalRpcCache;
+ ConcurrentMap mockRpcCache;
- ConcurrentMap concurrentMapMock = mock(ConcurrentMap.class);
+ @Before
+ public void setUp() throws Exception{
+ clusterService = mock(IClusterGlobalServices.class);
+ routingTable = new RoutingTableImpl<RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>, String>();
+ mockGlobalRpcCache = new ConcurrentHashMap<>();
+ mockRpcCache = new ConcurrentHashMap<>();
+ createRoutingTableCache();
+ }
+ @After
+ public void tearDown(){
+ reset(clusterService);
+ mockGlobalRpcCache = null;
+ mockRpcCache = null;
+ }
- @Test
- public void testAddGlobalRoute() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ @Test
+ public void addGlobalRoute_ValidArguments_ShouldAdd() throws Exception {
- 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);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ final String expectedRoute = "172.27.12.1:5000";
+ routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
- Set<String> globalService = new HashSet<String>();
- globalService.add("172.27.12.1:5000");
+ ConcurrentMap latestCache = routingTable.getGlobalRpcCache();
+ Assert.assertEquals(mockGlobalRpcCache, latestCache);
+ Assert.assertEquals(expectedRoute, latestCache.get(routeIdentifier));
+ }
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ @Test (expected = RoutingTable.DuplicateRouteException.class)
+ public void addGlobalRoute_DuplicateRoute_ShouldThrow() throws Exception{
- Assert.assertEquals(concurrentMap,latestCache);
+ Assert.assertNotNull(mockGlobalRpcCache);
- Set<String> servicesGlobal = (Set<String>)latestCache.get(routeIdentifier);
- Assert.assertEquals(servicesGlobal.size(),1);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ routingTable.addGlobalRoute(routeIdentifier, new String());
+ routingTable.addGlobalRoute(routeIdentifier, new String());
+ }
- Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+ @Test
+ public void getGlobalRoute_ExistingRouteId_ShouldReturnRoute() throws Exception {
- }
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ String expectedRoute = "172.27.12.1:5000";
- @Test
- public void testGetRoutes() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
- 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);
+ String actualRoute = (String) routingTable.getGlobalRoute(routeIdentifier);
+ Assert.assertEquals(expectedRoute, actualRoute);
+ }
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ @Test
+ public void getGlobalRoute_NonExistentRouteId_ShouldReturnNull() throws Exception {
- String globalService = "172.27.12.1:5000";
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ String actualRoute = (String) routingTable.getGlobalRoute(routeIdentifier);
+ Assert.assertNull(actualRoute);
+ }
- Assert.assertEquals(concurrentMap,latestCache);
+ @Test
+ public void removeGlobalRoute_ExistingRouteId_ShouldRemove() throws Exception {
- Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
+ ConcurrentMap cache = routingTable.getGlobalRpcCache();
+ Assert.assertTrue(cache.size() == 0);
+ routingTable.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ Assert.assertTrue(cache.size() == 1);
- Assert.assertEquals(servicesGlobal.size(),1);
- Iterator<String> iterator = servicesGlobal.iterator();
- while(iterator.hasNext()){
- Assert.assertEquals(iterator.next(),"172.27.12.1:5000");
- }
+ routingTable.removeGlobalRoute(routeIdentifier);
+ Assert.assertTrue(cache.size() == 0);
+ }
- }
- @Test
- public void testRegisterRouteChangeListener() throws Exception {
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0);
- rti.registerRouteChangeListener(new RouteChangeListenerImpl());
+ @Test
+ public void removeGlobalRoute_NonExistentRouteId_ShouldDoNothing() throws Exception {
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),0); //old should not work
- //what about the new approach - using whiteboard pattern
- rti.setRouteChangeListener(new RouteChangeListenerImpl());
+ Assert.assertNotNull(mockGlobalRpcCache);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- Assert.assertEquals(rti.getRegisteredRouteChangeListeners().size(),1); //should not work
+ ConcurrentMap cache = routingTable.getGlobalRpcCache();
+ Assert.assertTrue(cache.size() == 0);
+ routingTable.removeGlobalRoute(routeIdentifier);
+ Assert.assertTrue(cache.size() == 0);
- }
- @Test
- public void testRemoveGlobalRoute()throws Exception {
+ }
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ @Test
+ public void addRoute_ForNewRouteId_ShouldAddRoute() throws Exception {
+ Assert.assertTrue(mockRpcCache.size() == 0);
- 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);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
- rti.addGlobalRoute(routeIdentifier, "172.27.12.1:5000");
+ routingTable.addRoute(routeId, new String());
+ Assert.assertTrue(mockRpcCache.size() == 1);
- String globalService = "172.27.12.1:5000";
+ Set<String> routes = routingTable.getRoutes(routeId);
+ Assert.assertEquals(1, routes.size());
+ }
- when(concurrentMap.get(routeIdentifier)).thenReturn(globalService);
- ConcurrentMap latestCache = rti.getRoutingTableCache();
+ @Test
+ public void addRoute_ForExistingRouteId_ShouldAppendRoute() throws Exception {
- Assert.assertEquals(concurrentMap,latestCache);
+ Assert.assertTrue(mockRpcCache.size() == 0);
- Set<String> servicesGlobal = rti.getRoutes(routeIdentifier);
+ RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5955";
+ String route_2 = "10.0.0.2:5955";
- Assert.assertEquals(servicesGlobal.size(),1);
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
- Assert.assertEquals(servicesGlobal.iterator().next(),"172.27.12.1:5000");
+ Assert.assertTrue(mockRpcCache.size() == 1);
- rti.removeGlobalRoute(routeIdentifier);
+ Set<String> routes = routingTable.getRoutes(routeId);
+ Assert.assertEquals(2, routes.size());
+ Assert.assertTrue(routes.contains(route_1));
+ Assert.assertTrue(routes.contains(route_2));
+ }
- Assert.assertNotNull(rti.getRoutes(routeIdentifier));
+ @Test
+ public void addRoute_UsingMultipleThreads_ShouldNotOverwrite(){
+ ExecutorService threadPool = Executors.newCachedThreadPool();
+ int numOfRoutesToAdd = 100;
+ String routePrefix_1 = "10.0.0.1:555";
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ threadPool.submit(addRoutes(numOfRoutesToAdd, routePrefix_1, routeId));
+ String routePrefix_2 = "10.0.0.1:556";
+ threadPool.submit(addRoutes(numOfRoutesToAdd, routePrefix_2, routeId));
+ // wait for all tasks to complete; timeout in 10 sec
+ threadPool.shutdown();
+ try {
+ threadPool.awaitTermination(10, TimeUnit.SECONDS); //
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
- private ConcurrentMap createRoutingTableCache() throws Exception {
+ Assert.assertEquals(2*numOfRoutesToAdd, routingTable.getRoutes(routeId).size());
+ }
- //here init
- Component c = mock(Component.class);
+ @Test(expected = NullPointerException.class)
+ public void addRoute_NullRouteId_shouldThrowNpe() throws Exception {
- when(ics.existCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(false);
+ routingTable.addRoute(null, new String());
+ }
- when(ics.createCache(RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).thenReturn(concurrentMapMock);
- rti.setClusterGlobalServices(this.ics);
- rti.init(c);
+ @Test(expected = NullPointerException.class)
+ public void addRoute_NullRoute_shouldThrowNpe() throws Exception{
- Assert.assertEquals(concurrentMapMock,rti.getRoutingTableCache() );
- return concurrentMapMock;
+ routingTable.addRoute(getRouteIdentifier(), null);
+ }
- }
+ @Test (expected = UnsupportedOperationException.class)
+ public void getRoutes_Call_ShouldReturnImmutableCopy() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, new String());
+ Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
- @Test
- public void testCreateRoutingTableCacheReturnExistingCache() throws Exception {
- ConcurrentMap concurrentMap = createRoutingTableCache();
+ routes.add(new String()); //can not be modified; should throw
+ }
- //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);
+ @Test
+ public void getRoutes_With2RoutesFor1RouteId_ShouldReturnASetWithSize2() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ routingTable.addRoute(routeId, "10.0.0.1:5555");
+ routingTable.addRoute(routeId, "10.0.0.2:5555");
- when(ics.getCache(
- RoutingTableImpl.ROUTING_TABLE_GLOBAL_CACHE)).thenReturn(concurrentMap);
+ Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
+ Assert.assertEquals(2, routes.size());
+ }
- //here init
- Component c = mock(Component.class);
+ @Test
+ public void getLastAddedRoute_WhenMultipleRoutesExists_ShouldReturnLatestRoute()
+ throws Exception {
- rti.init(c);
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
+ String route_2 = "10.0.0.2:5555";
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
- Assert.assertEquals(concurrentMap,rti.getRoutingTableCache());
+ Assert.assertEquals(route_2, routingTable.getLastAddedRoute(routeId));
+ }
+ @Test
+ public void removeRoute_WhenMultipleRoutesExist_RemovesGivenRoute() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
+ String route_2 = "10.0.0.2:5555";
+ routingTable.addRoute(routeId, route_1);
+ routingTable.addRoute(routeId, route_2);
+ Assert.assertEquals(2, routingTable.getRoutes(routeId).size());
+ routingTable.removeRoute(routeId, route_1);
+ Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
- }
+ }
- private class RouteChangeListenerImpl<I,R> implements RouteChangeListener<I,R>{
+ @Test
+ public void removeRoute_WhenOnlyOneRouteExists_RemovesRouteId() throws Exception{
+ Assert.assertNotNull(routingTable);
+ RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
+ String route_1 = "10.0.0.1:5555";
- @Override
- public void onRouteUpdated(I key, R new_value) {
- //To change body of implemented methods use File | Settings | File Templates.
- }
+ routingTable.addRoute(routeId, route_1);
+ Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
- @Override
- public void onRouteDeleted(I key) {
- //To change body of implemented methods use File | Settings | File Templates.
- }
- }
+ routingTable.removeRoute(routeId, route_1);
+ ConcurrentMap cache = routingTable.getRpcCache();
+ Assert.assertFalse(cache.containsKey(routeId));
+
+ }
+ /*
+ * Private helper methods
+ */
+ private void createRoutingTableCache() throws Exception {
+
+ //here init
+ Component c = mock(Component.class);
+
+ when(clusterService.existCache(
+ RoutingTableImpl.GLOBALRPC_CACHE)).thenReturn(false);
+
+ when(clusterService.createCache(RoutingTableImpl.GLOBALRPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).
+ thenReturn(mockGlobalRpcCache);
+
+ when(clusterService.existCache(
+ RoutingTableImpl.RPC_CACHE)).thenReturn(false);
+
+ when(clusterService.createCache(RoutingTableImpl.RPC_CACHE,
+ EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL))).
+ thenReturn(mockRpcCache);
+
+ doNothing().when(clusterService).tbegin();
+ doNothing().when(clusterService).tcommit();
+
+ routingTable.setClusterGlobalServices(this.clusterService);
+ routingTable.init(c);
+
+ Assert.assertEquals(mockGlobalRpcCache, routingTable.getGlobalRpcCache());
+ Assert.assertEquals(mockRpcCache, routingTable.getRpcCache());
+ }
+
+ private RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> getRouteIdentifier(){
+ 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);
+
+ return routeIdentifier;
+ }
+
+ private Runnable addRoutes(final int numRoutes, final String routePrefix, final RpcRouter.RouteIdentifier routeId){
+ return new Runnable() {
+ @Override
+ public void run() {
+ for (int i=0;i<numRoutes;i++){
+ String route = routePrefix + i;
+ try {
+ routingTable.addRoute(routeId, route);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+ }
}
*/
package org.opendaylight.controller.sal.binding.api.data;
-import java.util.concurrent.Future;
-
import org.opendaylight.controller.md.sal.common.api.data.DataChangePublisher;
import org.opendaylight.controller.md.sal.common.api.data.DataModificationTransactionFactory;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
import org.opendaylight.controller.sal.binding.api.BindingAwareService;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.DataRoot;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
/**
* DataBrokerService provides unified access to the data stores available in the
* system.
- *
- *
+ *
+ *
* @see DataProviderService
*/
public interface DataBrokerService extends //
DataModificationTransactionFactory<InstanceIdentifier<? extends DataObject>, DataObject>, //
DataReader<InstanceIdentifier<? extends DataObject>, DataObject>, //
DataChangePublisher<InstanceIdentifier<? extends DataObject>, DataObject, DataChangeListener> {
-
- /**
- * Returns a data from specified Data Store.
- *
- * Returns all the data visible to the consumer from specified Data Store.
- *
- * @param <T>
- * Interface generated from YANG module representing root of data
- * @param store
- * Identifier of the store, from which will be data retrieved
- * @return data visible to the consumer
- */
- @Deprecated
- <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType);
-
- /**
- * Returns a filtered subset of data from specified Data Store.
- *
- * <p>
- * The filter is modeled as an hierarchy of Java TOs starting with
- * implementation of {@link DataRoot} representing data root. The semantics
- * of the filter tree is the same as filter semantics defined in the NETCONF
- * protocol for rpc operations <code>get</code> and <code>get-config</code>
- * in Section 6 of RFC6241.
- *
- *
- * @see http://tools.ietf.org/html/rfc6241#section-6
- * @param <T>
- * Interface generated from YANG module representing root of data
- * @param store
- * Identifier of the store, from which will be data retrieved
- * @param filter
- * Data tree filter similar to the NETCONF filter
- * @return
- */
- @Deprecated
- <T extends DataRoot> T getData(DataStoreIdentifier store, T filter);
-
- /**
- * Returns a candidate data which are not yet commited.
- *
- *
- * @param <T>
- * Interface generated from YANG module representing root of data
- * @param store
- * Identifier of the store, from which will be data retrieved
- * @return
- */
- @Deprecated
- <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType);
-
- /**
- * Returns a filtered subset of candidate data from specified Data Store.
- *
- * <p>
- * The filter is modeled as an hierarchy of {@link Node} starting with
- * {@link CompositeNode} representing data root. The semantics of the filter
- * tree is the same as filter semantics defined in the NETCONF protocol for
- * rpc operations <code>get</code> and <code>get-config</code> in Section 6
- * of RFC6241.
- *
- *
- * @see http://tools.ietf.org/html/rfc6241#section-6
- * @param <T>
- * Interface generated from YANG module representing root of data
- * @param store
- * Identifier of the store, from which will be data retrieved
- * @param filter
- * A filter data root
- * @return
- */
- @Deprecated
- <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter);
-
- /**
- *
- * @param <T>
- * Interface generated from YANG module representing root of data
- * @param store
- * Identifier of the store, in which will be the candidate data
- * modified
- * @param changeSet
- * Modification of data tree.
- * @return Result object containing the modified data tree if the operation
- * was successful, otherwise list of the encountered errors.
- */
- @Deprecated
- RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store, DataRoot changeSet);
-
- /**
- * Initiates a two-phase commit of candidate data.
- *
- * <p>
- * The {@link Consumer} could initiate a commit of candidate data
- *
- * <p>
- * The successful commit changes the state of the system and may affect
- * several components.
- *
- * <p>
- * The effects of successful commit of data are described in the
- * specifications and YANG models describing the {@link Provider} components
- * of controller. It is assumed that {@link Consumer} has an understanding
- * of this changes.
- *
- *
- * @see DataCommitHandler for further information how two-phase commit is
- * processed.
- * @param store
- * Identifier of the store, where commit should occur.
- * @return Result of the commit, containing success information or list of
- * encountered errors, if commit was not successful.
- */
- @Deprecated
- Future<RpcResult<Void>> commit(DataStoreIdentifier store);
-
- @Deprecated
- DataObject getData(InstanceIdentifier<? extends DataObject> data);
-
- @Deprecated
- DataObject getConfigurationData(InstanceIdentifier<?> data);
/**
* Creates a data modification transaction.
- *
+ *
* @return new blank data modification transaction.
*/
- DataModificationTransaction beginTransaction();
-
- @Deprecated
- public void registerChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener);
-
- @Deprecated
- public void unregisterChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener);
+ @Override
+ DataModificationTransaction beginTransaction();
/**
- * Reads data subtree from configurational store.
- * (Store which is populated by consumer, which is usually used to
+ * Reads data subtree from configurational store.
+ * (Store which is populated by consumer, which is usually used to
* inject state into providers. E.g. Flow configuration)-
- *
+ *
*/
@Override
public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path);
-
+
/**
- * Reads data subtree from operational store.
- * (Store which is populated by providers, which is usually used to
+ * Reads data subtree from operational store.
+ * (Store which is populated by providers, which is usually used to
* capture state of providers. E.g. Topology)
- *
+ *
*/
@Override
public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path);
-
+
/**
- * Register a data change listener for particular subtree.
- *
+ * Register a data change listener for particular subtree.
+ *
* Callback is invoked each time data in subtree changes.
- *
+ *
*/
@Override
public ListenerRegistration<DataChangeListener> registerDataChangeListener(
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator-plugin</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
\r
import java.util.Map;
import java.util.Map.Entry;
-import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataBroker;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.impl.util.BindingAwareDataReaderRouter;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.DataRoot;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.util.DataObjectReadingUtil;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
}\r
\r
@Override\r
- @Deprecated\r
- public <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public <T extends DataRoot> T getData(DataStoreIdentifier store, T filter) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store, DataRoot changeSet) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public DataObject getData(InstanceIdentifier<? extends DataObject> data) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public DataObject getConfigurationData(InstanceIdentifier<?> data) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public void registerChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- @Deprecated\r
- public void unregisterChangeListener(InstanceIdentifier<? extends DataObject> path,\r
- DataChangeListener changeListener) {\r
- throw new UnsupportedOperationException("Deprecated");\r
- }\r
-\r
- @Override\r
- public void close() throws Exception {\r
+ public void close() {\r
\r
}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.binding.impl
-
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService
-import org.opendaylight.controller.sal.common.DataStoreIdentifier
-import org.opendaylight.yangtools.yang.binding.DataRoot
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener
-
-abstract class DeprecatedDataAPISupport implements DataProviderService {
-
- @Deprecated
- override commit(DataStoreIdentifier store) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override editCandidateData(DataStoreIdentifier store, DataRoot changeSet) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override <T extends DataRoot> getCandidateData(DataStoreIdentifier store, Class<T> rootType) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override getConfigurationData(InstanceIdentifier<?> data) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override <T extends DataRoot> getData(DataStoreIdentifier store, Class<T> rootType) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override <T extends DataRoot> T getData(DataStoreIdentifier store, T filter) {
- throw new UnsupportedOperationException("Deprecated")
- }
-
- @Deprecated
- override getData(InstanceIdentifier<? extends DataObject> path) {
- return readOperationalData(path);
- }
-
- override registerChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {
- }
-
- override unregisterChangeListener(InstanceIdentifier<? extends DataObject> path,
- DataChangeListener changeListener) {
- }
-
-}
DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
DataModificationTransaction target = biDataService.beginTransaction();
LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(),source.getIdentifier());
+ for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
+ target.removeConfigurationData(biEntry);
+ LOG.debug("Delete of Binding Configuration Data {} is translated to {}",entry,biEntry);
+ }
+ for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
+ target.removeOperationalData(biEntry);
+ LOG.debug("Delete of Binding Operational Data {} is translated to {}",entry,biEntry);
+ }
for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedConfigurationData()
.entrySet()) {
Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
target.putOperationalData(biEntry.getKey(), biEntry.getValue());
LOG.debug("Update of Binding Operational Data {} is translated to {}",entry,biEntry);
}
- for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
- target.removeConfigurationData(biEntry);
- LOG.debug("Delete of Binding Configuration Data {} is translated to {}",entry,biEntry);
- }
- for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
- target.removeOperationalData(biEntry);
- LOG.debug("Delete of Binding Operational Data {} is translated to {}",entry,biEntry);
- }
+
return target;
}
DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
.beginTransaction();
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
+ try {
+
+ InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
+ target.removeConfigurationData(baEntry);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry, e);
+ }
+ }
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
+ try {
+
+ InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
+ target.removeOperationalData(baEntry);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry, e);
+ }
+ }
for (Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
.getUpdatedConfigurationData().entrySet()) {
try {
LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
}
}
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
- try {
-
- InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
- target.removeConfigurationData(baEntry);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry, e);
- }
- }
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
- try {
-
- InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
- target.removeOperationalData(baEntry);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry, e);
- }
- }
return target;
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator-plugin</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>maven-sal-api-gen-plugin</artifactId>
- <version>${yangtools.version}</version>
+ <version>${yangtools.version}</version>
<type>jar</type>
</dependency>
</dependencies>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.binding.test.bugfix;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+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.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+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.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.RpcResult;
+
+import com.google.common.collect.ImmutableList;
+
+public class WriteParentReadChildTest extends AbstractDataServiceTest {
+
+ private static final String FLOW_ID = "1234";
+ private static final short TABLE_ID = (short) 0;
+ private static final String NODE_ID = "node:1";
+
+ private static final NodeKey NODE_KEY = new NodeKey(new NodeId(NODE_ID));
+ private static final FlowKey FLOW_KEY = new FlowKey(new FlowId(FLOW_ID));
+ private static final TableKey TABLE_KEY = new TableKey(TABLE_ID);
+
+ private static final InstanceIdentifier<Node> NODE_INSTANCE_ID_BA = InstanceIdentifier.builder(Nodes.class) //
+ .child(Node.class, NODE_KEY).toInstance();
+
+ private static final InstanceIdentifier<Table> TABLE_INSTANCE_ID_BA = //
+ InstanceIdentifier.builder(NODE_INSTANCE_ID_BA) //
+ .augmentation(FlowCapableNode.class).child(Table.class, TABLE_KEY).build();
+
+ private static final InstanceIdentifier<? extends DataObject> FLOW_INSTANCE_ID_BA = //
+ InstanceIdentifier.builder(TABLE_INSTANCE_ID_BA) //
+ .child(Flow.class, FLOW_KEY) //
+ .toInstance();
+ /**
+ *
+ * The scenario tests writing parent node, which also contains child items
+ * and then reading child directly, by specifying path to the child.
+ *
+ * Expected behaviour is child is returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void writeTableReadFlow() throws Exception {
+
+ DataModificationTransaction modification = baDataService.beginTransaction();
+
+ Flow flow = new FlowBuilder() //
+ .setKey(FLOW_KEY) //
+ .setMatch(new MatchBuilder() //
+ .setVlanMatch(new VlanMatchBuilder() //
+ .setVlanId(new VlanIdBuilder() //
+ .setVlanId(new VlanId(10)) //
+ .build()) //
+ .build()) //
+ .build()) //
+ .setInstructions(new InstructionsBuilder() //
+ .setInstruction(ImmutableList.<Instruction>builder() //
+ .build()) //
+ .build()) //
+ .build();
+
+ Table table = new TableBuilder()
+ .setKey(TABLE_KEY)
+ .setFlow(ImmutableList.of(flow))
+ .build();
+
+ modification.putConfigurationData(TABLE_INSTANCE_ID_BA, table);
+ RpcResult<TransactionStatus> ret = modification.commit().get();
+ assertNotNull(ret);
+ assertEquals(TransactionStatus.COMMITED, ret.getResult());
+
+ DataObject readedTable = baDataService.readConfigurationData(TABLE_INSTANCE_ID_BA);
+ assertNotNull("Readed table should not be nul.", readedTable);
+ assertTrue(readedTable instanceof Table);
+
+ DataObject readedFlow = baDataService.readConfigurationData(FLOW_INSTANCE_ID_BA);
+ assertNotNull("Readed flow should not be null.",readedFlow);
+ assertTrue(readedFlow instanceof Flow);
+ assertEquals(flow, readedFlow);
+
+ }
+}
\ No newline at end of file
<dependencies>
+ <!--Compile scopes for all testing dependencies are intentional-->
+ <!--This way, all testing dependencies can be transitively used by other integration test modules-->
+ <!--If the dependencies are test scoped, they are not visible to other maven modules depending on sal-binding-it-->
+ <!--TODO Create generic utilities(extract from this module) for integration tests on the controller-->
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>xtend-lib-osgi</artifactId>
<version>2.4.3</version>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-broker-impl</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
- <scope>test</scope>
+ <scope>compile</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
- <scope>test</scope>
+ <scope>compile</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-netconf-connector</artifactId>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-link-mvn</artifactId>
- <scope>test</scope>
+ <scope>compile</scope>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
*/
package org.opendaylight.controller.md.sal.binding.util;
-import java.util.concurrent.Future;
-
import org.opendaylight.controller.sal.binding.api.NotificationListener;
import org.opendaylight.controller.sal.binding.api.NotificationService;
import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.DataRoot;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.Notification;
import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import com.google.common.base.Preconditions;
return getNotificationBrokerChecked().registerNotificationListener(listener);
}
- @Override
- @Deprecated
- public <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType) {
- return getDataBrokerChecked().getData(store, rootType);
- }
-
- @Override
- @Deprecated
- public <T extends DataRoot> T getData(DataStoreIdentifier store, T filter) {
- return getDataBrokerChecked().getData(store, filter);
- }
-
- @Override
- @Deprecated
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType) {
- return getDataBrokerChecked().getCandidateData(store, rootType);
- }
-
- @Override
- @Deprecated
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter) {
- return getDataBrokerChecked().getCandidateData(store, filter);
- }
-
- @Override
- @Deprecated
- public RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store, DataRoot changeSet) {
- return getDataBrokerChecked().editCandidateData(store, changeSet);
- }
-
- @Override
- @Deprecated
- public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {
- return getDataBrokerChecked().commit(store);
- }
-
- @Override
- @Deprecated
- public DataObject getData(InstanceIdentifier<? extends DataObject> data) {
- return getDataBrokerChecked().getData(data);
- }
-
- @Override
- @Deprecated
- public DataObject getConfigurationData(InstanceIdentifier<?> data) {
- return getDataBrokerChecked().getConfigurationData(data);
- }
-
@Override
public DataModificationTransaction beginTransaction() {
return getDataBrokerChecked().beginTransaction();
}
- @Override
- @Deprecated
- public void registerChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {
- getDataBrokerChecked().registerChangeListener(path, changeListener);
- }
-
- @Override
- @Deprecated
- public void unregisterChangeListener(InstanceIdentifier<? extends DataObject> path,
- DataChangeListener changeListener) {
- getDataBrokerChecked().unregisterChangeListener(path, changeListener);
- }
-
@Override
@Deprecated
public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
* this is responsibility of the coordinator (broker or some component of the
* broker).
*
- * The commit handlers are responsible for changing the internal state of the
- * provider to reflect the commited changes in data.
+ * Commit handlers are responsible for changing the internal state of the
+ * provider to reflect the committed changes in data.
*
* <h3>Two-phase commit</h3>
*
.addAll(transaction.getCreatedOperationalData().keySet())
.addAll(transaction.getRemovedOperationalData()).build();
- log.trace("Transaction: {} Affected Subtrees:", transactionId, changedPaths);
+ log.trace("Transaction: {} Affected Subtrees: {}", transactionId, changedPaths);
// The transaction has no effects, let's just shortcut it
if (changedPaths.isEmpty()) {
captureFinalState(listeners);
- log.trace("Transaction: {} Notifying listeners.");
+ log.trace("Transaction: {} Notifying listeners.", transactionId);
publishDataChangeEvent(listeners);
return Rpcs.<TransactionStatus> getRpcResult(true, TransactionStatus.COMMITED,
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+/**
+ * @deprecated Use {@link com.google.common.util.concurrent.Futures} instead.
+ */
+@Deprecated
public class Futures {
private Futures() {
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator-plugin</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
/**
* Core component of the SAL layer responsible for wiring the SAL consumers.
- *
+ *
* The responsibility of the broker is to maintain registration of SAL
* functionality {@link Consumer}s and {@link Provider}s, store provider and
* consumer specific context and functionality registration via
* {@link ConsumerSession} and provide access to infrastructure services, which
* removes direct dependencies between providers and consumers.
- *
- *
+ *
+ *
* <h3>Infrastructure services</h3> Some examples of infrastructure services:
- *
+ *
* <ul>
* <li>RPC Invocation - see {@link ConsumerSession#rpc(QName, CompositeNode)},
* {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)} and
* <li>Data Store access and modification - see {@link DataBrokerService} and
* {@link DataProviderService}
* </ul>
- *
+ *
* The services are exposed via session.
- *
+ *
* <h3>Session-based access</h3>
- *
+ *
* The providers and consumers needs to register in order to use the
* binding-independent SAL layer and to expose functionality via SAL layer.
- *
+ *
* For more information about session-based access see {@link ConsumerSession}
* and {@link ProviderSession}
- *
- *
- *
+ *
+ *
+ *
*/
public interface Broker {
/**
* Registers the {@link Consumer}, which will use the SAL layer.
- *
+ *
* <p>
* During the registration, the broker obtains the initial functionality
* from consumer, using the {@link Consumer#getConsumerFunctionality()}, and
* register that functionality into system and concrete infrastructure
* services.
- *
+ *
* <p>
* Note that consumer could register additional functionality at later point
* by using service and functionality specific APIs.
- *
+ *
* <p>
* The consumer is required to use returned session for all communication
* with broker or one of the broker services. The session is announced to
* the consumer by invoking
* {@link Consumer#onSessionInitiated(ConsumerSession)}.
- *
+ *
* @param cons
* Consumer to be registered.
* @param context
/**
* Registers the {@link Provider}, which will use the SAL layer.
- *
+ *
* <p>
* During the registration, the broker obtains the initial functionality
* from consumer, using the {@link Provider#getProviderFunctionality()}, and
* register that functionality into system and concrete infrastructure
* services.
- *
+ *
* <p>
* Note that consumer could register additional functionality at later point
* by using service and functionality specific APIs (e.g.
* {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}
- *
+ *
* <p>
* The consumer is <b>required to use</b> returned session for all
* communication with broker or one of the broker services. The session is
* announced to the consumer by invoking
* {@link Provider#onSessionInitiated(ProviderSession)}.
- *
- *
+ *
+ *
* @param prov
* Provider to be registered.
* @param context
/**
* {@link Consumer} specific access to the SAL functionality.
- *
+ *
* <p>
* ConsumerSession is {@link Consumer}-specific access to the SAL
* functionality and infrastructure services.
- *
+ *
* <p>
* The session serves to store SAL context (e.g. registration of
* functionality) for the consumer and provides access to the SAL
* infrastructure services and other functionality provided by
* {@link Provider}s.
- *
- *
- *
+ *
+ *
+ *
*/
public interface ConsumerSession {
/**
* Sends an RPC to other components registered to the broker.
- *
+ *
* @see RpcImplementation
* @param rpc
* Name of RPC
/**
* Returns a session specific instance (implementation) of requested
* service
- *
+ *
* @param service
* Broker service
* @return Session specific implementation of service
/**
* Closes a session between consumer and broker.
- *
+ *
* <p>
* The close operation unregisters a consumer and remove all registered
* functionality of the consumer from the system.
- *
+ *
*/
void close();
}
/**
* {@link Provider} specific access to the SAL functionality.
- *
+ *
* <p>
* ProviderSession is {@link Provider}-specific access to the SAL
* functionality and infrastructure services, which also allows for exposing
* the provider's functionality to the other {@link Consumer}s.
- *
+ *
* <p>
* The session serves to store SAL context (e.g. registration of
* functionality) for the providers and exposes access to the SAL
* infrastructure services, dynamic functionality registration and any other
* functionality provided by other {@link Provider}s.
- *
+ *
*/
public interface ProviderSession extends ConsumerSession {
/**
* Registers an implementation of the rpc.
- *
+ *
* <p>
* The registered rpc functionality will be available to all other
* consumers and providers registered to the broker, which are aware of
* the {@link QName} assigned to the rpc.
- *
+ *
* <p>
* There is no assumption that rpc type is in the set returned by
* invoking {@link RpcImplementation#getSupportedRpcs()}. This allows
* for dynamic rpc implementations.
- *
+ *
* @param rpcType
* Name of Rpc
* @param implementation
/**
* Closes a session between provider and SAL.
- *
+ *
* <p>
* The close operation unregisters a provider and remove all registered
* functionality of the provider from the system.
boolean isClosed();
Set<QName> getSupportedRpcs();
-
+
ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
}
public interface RpcRegistration extends Registration<RpcImplementation> {
QName getType();
+
+ @Override
+ void close();
}
public interface RoutedRpcRegistration extends RpcRegistration,
--- /dev/null
+package org.opendaylight.controller.sal.core.api;
+
+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;
+
+public interface RoutedRpcDefaultImplementation {
+
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input);
+
+}
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
/**
- * {@link Provider}'s implementation of rpc.
- *
- * In order to expose the rpc to other components, the provider MUST register
- * concrete implementation of this interface
- *
+ * {@link Provider}'s implementation of an RPC.
+ *
+ * In order to expose an RPC to other components, the provider MUST register
+ * a concrete implementation of this interface.
+ *
* The registration could be done by :
* <ul>
* <li>returning an instance of implementation in the return value of
* arguments to the
* {@link ProviderSession#addRpcImplementation(QName, RpcImplementation)}
* </ul>
- *
+ *
* The simplified process of the invocation of rpc is following:
- *
+ *
* <ol>
* <li> {@link Consumer} invokes
* {@link ConsumerSession#rpc(QName, CompositeNode)}
* {@link RpcResult}
* <li> {@link Broker} returns the {@link RpcResult} to {@link Consumer}
* </ol>
- *
- *
+ *
+ *
*/
public interface RpcImplementation extends Provider.ProviderFunctionality {
/**
* A set of rpc types supported by implementation.
- *
+ *
* The set of rpc {@link QName}s which are supported by this implementation.
* This set is used, when {@link Provider} is registered to the SAL, to
* register and expose the implementation of the returned rpcs.
- *
+ *
* @return Set of QNames identifying supported RPCs
*/
Set<QName> getSupportedRpcs();
/**
* Invokes a implementation of specified rpc.
- *
- *
+ *
+ *
* @param rpc
* Rpc to be invoked
* @param input
* Input data for rpc.
- *
+ *
* @throws IllegalArgumentException
* <ul>
* <li>If rpc is null.
ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener);
RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation);
+
+ /**
+ * Sets this RoutedRpc Implementation as a delegate rpc provider and will be asked to invoke rpc if the
+ * current provider can't service the rpc request
+ *
+ * @param defaultImplementation
+ * Provider's implementation of RPC functionality
+ */
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation);
}
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator-plugin</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
broker.setRouter(new SchemaAwareRpcBroker("/", SchemaContextProviders.fromSchemaService(schemaService)));
dataService = new DataBrokerImpl();
- dataService.setExecutor(broker.getExecutor());
+ //dataService.setExecutor(broker.getExecutor());
dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener
import org.opendaylight.controller.sal.core.api.Broker
import org.opendaylight.controller.sal.core.api.Consumer
import org.opendaylight.controller.sal.core.api.Provider
-import org.opendaylight.controller.sal.core.api.RpcImplementation
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
-import org.opendaylight.controller.sal.core.api.RpcRoutingContext
-import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.common.RpcResult
import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
import org.osgi.framework.BundleContext
import org.slf4j.LoggerFactory
+import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry
+import org.opendaylight.controller.sal.core.api.RpcImplementation
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener
+import org.opendaylight.controller.sal.core.api.RpcRoutingContext
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation
public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable {
private static val log = LoggerFactory.getLogger(BrokerImpl);
override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
router.addRoutedRpcImplementation(rpcType,implementation);
}
-
+
+ override setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
+ router.setRoutedRpcDefaultDelegate(defaultImplementation);
+ }
+
override addRpcRegistrationListener(RpcRegistrationListener listener) {
return router.addRpcRegistrationListener(listener);
}
import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
}
@Override
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
+ rpcs.setRoutedRpcDefaultDelegate(defaultImplementation);
+ }
+
+ @Override
public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
throws IllegalArgumentException {
return rpcs.addRpcImplementation(rpcType, implementation);
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Future;
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.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
DataStore, //
- SchemaServiceListener, //
SchemaContextListener, //
AutoCloseable {
private NormalizedDataModification prepareMergedTransaction(
DataModification<InstanceIdentifier, CompositeNode> original) {
NormalizedDataModification normalized = new NormalizedDataModification(original);
- for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
- normalized.putConfigurationData(entry.getKey(), entry.getValue());
- }
- for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
- normalized.putOperationalData(entry.getKey(), entry.getValue());
- }
+ LOG.trace("Transaction: {} Removed Configuration {}, Removed Operational {}", original.getIdentifier(),
+ original.getRemovedConfigurationData(), original.getRemovedConfigurationData());
+ LOG.trace("Transaction: {} Created Configuration {}, Created Operational {}", original.getIdentifier(),
+ original.getCreatedConfigurationData().entrySet(), original.getCreatedOperationalData().entrySet());
+ LOG.trace("Transaction: {} Updated Configuration {}, Updated Operational {}", original.getIdentifier(),
+ original.getUpdatedConfigurationData().entrySet(), original.getUpdatedOperationalData().entrySet());
+
for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
normalized.deepRemoveConfigurationData(entry);
}
for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
normalized.deepRemoveOperationalData(entry);
}
+ for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
+ normalized.putDeepConfigurationData(entry.getKey(), entry.getValue());
+ }
+ for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
+ normalized.putDeepOperationalData(entry.getKey(), entry.getValue());
+ }
return normalized;
}
private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
+ private final String CONFIGURATIONAL_DATA_STORE_MARKER = "configurational";
+ private final String OPERATIONAL_DATA_STORE_MARKER = "operational";
private final Object identifier;
private TransactionStatus status;
}
}
+ public void putDeepConfigurationData(InstanceIdentifier entryKey, CompositeNode entryData) {
+ this.putCompositeNodeData(entryKey, entryData, CONFIGURATIONAL_DATA_STORE_MARKER);
+ }
+
+ public void putDeepOperationalData(InstanceIdentifier entryKey, CompositeNode entryData) {
+ this.putCompositeNodeData(entryKey, entryData, OPERATIONAL_DATA_STORE_MARKER);
+ }
+
@Override
public Object getIdentifier() {
return this.identifier;
CompositeNode modified) {
return mergeData(path, stored, modified, false);
}
- }
+ private void putData(InstanceIdentifier entryKey, CompositeNode entryData, String dataStoreIdentifier) {
+ if (dataStoreIdentifier != null && entryKey != null && entryData != null) {
+ switch (dataStoreIdentifier) {
+ case (CONFIGURATIONAL_DATA_STORE_MARKER):
+ this.putConfigurationData(entryKey, entryData);
+ break;
+ case (OPERATIONAL_DATA_STORE_MARKER):
+ this.putOperationalData(entryKey, entryData);
+ break;
+
+ default:
+ LOG.error(dataStoreIdentifier + " is NOT valid DataStore switch marker");
+ throw new RuntimeException(dataStoreIdentifier + " is NOT valid DataStore switch marker");
+ }
+ }
+ }
+
+ private void putCompositeNodeData(InstanceIdentifier entryKey, CompositeNode entryData,
+ String dataStoreIdentifier) {
+ this.putData(entryKey, entryData, dataStoreIdentifier);
+
+ for (Node<?> child : entryData.getChildren()) {
+ InstanceIdentifier subEntryId = InstanceIdentifier.builder(entryKey).node(child.getNodeType())
+ .toInstance();
+ if (child instanceof CompositeNode) {
+ DataSchemaNode subSchema = schemaNodeFor(subEntryId);
+ CompositeNode compNode = (CompositeNode) child;
+ InstanceIdentifier instanceId = null;
+
+ if (subSchema instanceof ListSchemaNode) {
+ ListSchemaNode listSubSchema = (ListSchemaNode) subSchema;
+ Map<QName, Object> mapOfSubValues = this.getValuesFromListSchema(listSubSchema,
+ (CompositeNode) child);
+ if (mapOfSubValues != null) {
+ instanceId = InstanceIdentifier.builder(entryKey)
+ .nodeWithKey(listSubSchema.getQName(), mapOfSubValues).toInstance();
+ }
+ } else if (subSchema instanceof ContainerSchemaNode) {
+ ContainerSchemaNode containerSchema = (ContainerSchemaNode) subSchema;
+ instanceId = InstanceIdentifier.builder(entryKey).node(subSchema.getQName()).toInstance();
+ }
+ if (instanceId != null) {
+ this.putCompositeNodeData(instanceId, compNode, dataStoreIdentifier);
+ }
+ }
+ }
+ }
+
+ private Map<QName, Object> getValuesFromListSchema(ListSchemaNode listSchema, CompositeNode entryData) {
+ List<QName> keyDef = listSchema.getKeyDefinition();
+ if (keyDef != null && !keyDef.isEmpty()) {
+ Map<QName, Object> map = new HashMap<QName, Object>();
+ for (QName key : keyDef) {
+ List<Node<?>> data = entryData.get(key);
+ if (data != null && !data.isEmpty()) {
+ for (Node<?> nodeData : data) {
+ if (nodeData instanceof SimpleNode<?>) {
+ map.put(key, data.get(0).getValue());
+ }
+ }
+ }
+ }
+ return map;
+ }
+ return null;
+ }
+ }
}
import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
-public class SchemaAwareRpcBroker implements RpcRouter, Identifiable<String> {
+public class SchemaAwareRpcBroker implements RpcRouter, Identifiable<String>, RoutedRpcDefaultImplementation {
private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareRpcBroker.class);
private final ConcurrentMap<QName, RpcImplementation> implementations = new ConcurrentHashMap<>();
private RpcImplementation defaultImplementation;
private SchemaContextProvider schemaProvider;
+ private RoutedRpcDefaultImplementation defaultDelegate;
public SchemaAwareRpcBroker(String identifier, SchemaContextProvider schemaProvider) {
super();
this.schemaProvider = schemaProvider;
}
+ public RoutedRpcDefaultImplementation getRoutedRpcDefaultDelegate() {
+ return defaultDelegate;
+ }
+
@Override
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultDelegate) {
+ this.defaultDelegate = defaultDelegate;
+ }
+
+ @Override
public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
checkArgument(rpcType != null, "RPC Type should not be null");
checkArgument(implementation != null, "RPC Implementatoin should not be null");
return ret;
}
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
+ checkState(defaultDelegate != null);
+ return defaultDelegate.invokeRpc(rpc, identifier, input);
+ }
+
private static abstract class RoutingStrategy implements Identifiable<QName> {
private final QName identifier;
SimpleNode<?> routeContainer = inputContainer.getFirstSimpleByName(strategy.getLeaf());
checkArgument(routeContainer != null, "Leaf %s must be set with value", strategy.getLeaf());
Object route = routeContainer.getValue();
+ checkArgument(route instanceof InstanceIdentifier);
RpcImplementation potential = null;
if (route != null) {
RoutedRpcRegImpl potentialReg = implementations.get(route);
}
}
if (potential == null) {
- potential = defaultDelegate;
+ return router.invokeRpc(rpc, (InstanceIdentifier) route, input);
}
checkState(potential != null, "No implementation is available for rpc:%s path:%s", rpc, route);
return potential.invokeRpc(rpc, input);
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
-import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
+import org.opendaylight.controller.sal.core.api.*;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
return getDelegate().addRoutedRpcImplementation(rpcType, implementation);
}
- @Override
+ @Override
+ public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
+ getDelegate().setRoutedRpcDefaultDelegate(defaultImplementation);
+ }
+
+ @Override
public <L extends RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> ListenerRegistration<L> registerRouteChangeListener(L listener) {
return getDelegate().registerRouteChangeListener(listener);
}
<artifactId>sal-parent</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>
- <properties>
- <netconf.version>0.2.4-SNAPSHOT</netconf.version>
- </properties>
+
<artifactId>sal-netconf-connector</artifactId>
<scm>
<connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>yang-jmx-generator-plugin</artifactId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>${config.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>maven-sal-api-gen-plugin</artifactId>
- <version>${yangtools.version}</version>
+ <version>${yangtools.version}</version>
<type>jar</type>
</dependency>
</dependencies>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<id>sal-remote</id>
import org.opendaylight.controller.sal.connector.remoterpc.*;
import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.osgi.framework.BundleContext;
/**
ClientImpl clientImpl = new ClientImpl();
- RoutingTableProvider provider = new RoutingTableProvider(bundleContext,serverImpl);
+ RoutingTableProvider provider = new RoutingTableProvider(bundleContext);//,serverImpl);
- RemoteRpcProvider facade = new RemoteRpcProvider(serverImpl, clientImpl);
-
- facade.setRoutingTableProvider(provider );
-
- broker.registerProvider(facade, bundleContext);
- return facade;
- }
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
+ facade.setRoutingTableProvider(provider );
+ facade.setContext(bundleContext);
+ facade.setRpcProvisionRegistry((RpcProvisionRegistry) broker);
+
+ broker.registerProvider(facade, bundleContext);
+ return facade;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
}
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
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.opendaylight.controller.sal.connector.remoterpc.dto.Message;
import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zeromq.ZMQ;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
this.routingTableProvider = routingTableProvider;
}
- @Override
- public Set<QName> getSupportedRpcs(){
- //TODO: Find the entries from routing table
- return Collections.emptySet();
- }
-
@Override
public void start() {/*NOOPS*/}
* @param input payload for the remote service
* @return
*/
- @Override
public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl();
+ routeId.setType(rpc);
+
+ String address = lookupRemoteAddressForGlobalRpc(routeId);
+ return sendMessage(input, routeId, address);
+ }
+
+ /**
+ * Finds remote server that can execute this routed rpc and sends a message to it
+ * requesting execution.
+ * The call blocks until a response from remote server is received. Its upto
+ * the client of this API to implement a timeout functionality.
+ *
+ * @param rpc
+ * rpc to be called
+ * @param identifier
+ * instance identifier on which rpc is to be executed
+ * @param input
+ * payload
+ * @return
+ */
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
RouteIdentifierImpl routeId = new RouteIdentifierImpl();
routeId.setType(rpc);
+ routeId.setRoute(identifier);
+
+ String address = lookupRemoteAddressForRpc(routeId);
- String address = lookupRemoteAddress(routeId);
+ return sendMessage(input, routeId, address);
+ }
+ private RpcResult<CompositeNode> sendMessage(CompositeNode input, RouteIdentifierImpl routeId, String address) {
Message request = new Message.MessageBuilder()
.type(Message.MessageType.REQUEST)
.sender(Context.getInstance().getLocalUri())
collectErrors(e, errors);
return Rpcs.getRpcResult(false, null, errors);
}
-
}
/**
* @param routeId route identifier
* @return remote network address
*/
- private String lookupRemoteAddress(RpcRouter.RouteIdentifier routeId){
+ private String lookupRemoteAddressForGlobalRpc(RpcRouter.RouteIdentifier routeId){
checkNotNull(routeId, "route must not be null");
- Optional<RoutingTable<String, String>> routingTable = routingTableProvider.getRoutingTable();
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> routingTable = routingTableProvider.getRoutingTable();
checkNotNull(routingTable.isPresent(), "Routing table is null");
- Set<String> addresses = routingTable.get().getRoutes(routeId.toString());
- checkNotNull(addresses, "Address not found for route [%s]", routeId);
- checkState(addresses.size() == 1,
- "Multiple remote addresses found for route [%s], \nonly 1 expected", routeId); //its a global service.
+ String address = null;
+ try {
+ address = routingTable.get().getGlobalRoute(routeId);
+ } catch (RoutingTableException|SystemException e) {
+ _logger.error("Exception caught while looking up remote address " + e);
+ }
+ checkState(address != null, "Address not found for route [%s]", routeId);
+
+ return address;
+ }
+
+ /**
+ * Find address for the given route identifier in routing table
+ * @param routeId route identifier
+ * @return remote network address
+ */
+ private String lookupRemoteAddressForRpc(RpcRouter.RouteIdentifier routeId){
+ checkNotNull(routeId, "route must not be null");
+
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> routingTable = routingTableProvider.getRoutingTable();
+ checkNotNull(routingTable.isPresent(), "Routing table is null");
- String address = addresses.iterator().next();
- checkNotNull(address, "Address not found for route [%s]", routeId);
+ String address = routingTable.get().getLastAddedRoute(routeId);
+ checkState(address != null, "Address not found for route [%s]", routeId);
return address;
}
package org.opendaylight.controller.sal.connector.remoterpc;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
-public interface RemoteRpcClient extends RpcImplementation,AutoCloseable{
-
+public interface RemoteRpcClient extends AutoCloseable{
void setRoutingTableProvider(RoutingTableProvider provider);
package org.opendaylight.controller.sal.connector.remoterpc;
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
+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.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
+import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
-public class RemoteRpcProvider implements
- RemoteRpcServer,
- RemoteRpcClient,
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class RemoteRpcProvider implements
+ RpcImplementation,
+ RoutedRpcDefaultImplementation,
+ AutoCloseable,
Provider {
- private final ServerImpl server;
- private final ClientImpl client;
- private RoutingTableProvider provider;
+ private Logger _logger = LoggerFactory.getLogger(RemoteRpcProvider.class);
- @Override
- public void setRoutingTableProvider(RoutingTableProvider provider) {
- this.provider = provider;
- server.setRoutingTableProvider(provider);
- client.setRoutingTableProvider(provider);
+ private final ServerImpl server;
+ private final ClientImpl client;
+ private RoutingTableProvider routingTableProvider;
+ private final RpcListener listener = new RpcListener();
+ private final RoutedRpcListener routeChangeListener = new RoutedRpcListener();
+ private ProviderSession brokerSession;
+ private RpcProvisionRegistry rpcProvisionRegistry;
+ private BundleContext context;
+ private ServiceTracker clusterTracker;
+
+ public RemoteRpcProvider(ServerImpl server, ClientImpl client) {
+ this.server = server;
+ this.client = client;
+ }
+
+ public void setRoutingTableProvider(RoutingTableProvider provider) {
+ this.routingTableProvider = provider;
+ client.setRoutingTableProvider(provider);
+ }
+
+ public void setContext(BundleContext context){
+ this.context = context;
+ }
+
+ public void setRpcProvisionRegistry(RpcProvisionRegistry rpcProvisionRegistry){
+ this.rpcProvisionRegistry = rpcProvisionRegistry;
+ }
+
+ @Override
+ public void onSessionInitiated(ProviderSession session) {
+ brokerSession = session;
+ server.setBrokerSession(session);
+ start();
+ }
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ //TODO: Ask Tony if we need to get this from routing table
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
+ return client.invokeRpc(rpc, input);
+ }
+
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
+ return client.invokeRpc(rpc, identifier, input);
+ }
+
+ public void start() {
+ server.start();
+ client.start();
+ brokerSession.addRpcRegistrationListener(listener);
+ rpcProvisionRegistry.setRoutedRpcDefaultDelegate(this);
+ rpcProvisionRegistry.registerRouteChangeListener(routeChangeListener);
+
+ announceSupportedRpcs();
+ announceSupportedRoutedRpcs();
+ }
+
+ @Override
+ public void close() throws Exception {
+ unregisterSupportedRpcs();
+ unregisterSupportedRoutedRpcs();
+ server.close();
+ client.close();
+ }
+
+ public void stop() {
+ server.stop();
+ client.stop();
+ }
+
+ /**
+ * Add all the locally registered RPCs in the clustered routing table
+ */
+ private void announceSupportedRpcs(){
+ Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
+ for (QName rpc : currentlySupported) {
+ listener.onRpcImplementationAdded(rpc);
}
-
- @Override
- public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
- return client.invokeRpc(rpc, input);
+ }
+
+ /**
+ * Add all the locally registered Routed RPCs in the clustered routing table
+ */
+ private void announceSupportedRoutedRpcs(){
+
+ //TODO: announce all routed RPCs as well
+
+ }
+
+ /**
+ * Un-Register all the supported RPCs from clustered routing table
+ */
+ private void unregisterSupportedRpcs(){
+ Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
+ //TODO: remove all routed RPCs as well
+ for (QName rpc : currentlySupported) {
+ listener.onRpcImplementationRemoved(rpc);
}
-
+ }
+
+ /**
+ * Un-Register all the locally supported Routed RPCs from clustered routing table
+ */
+ private void unregisterSupportedRoutedRpcs(){
+
+ //TODO: remove all routed RPCs as well
+
+ }
+
+ private RoutingTable<RpcRouter.RouteIdentifier, String> getRoutingTable(){
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> routingTable =
+ routingTableProvider.getRoutingTable();
+
+ checkNotNull(routingTable.isPresent(), "Routing table is null");
+
+ return routingTable.get();
+ }
+
+ /**
+ * Listener for rpc registrations in broker
+ */
+ private class RpcListener implements RpcRegistrationListener {
+
@Override
- public Set<QName> getSupportedRpcs() {
- return client.getSupportedRpcs();
- }
-
-
- public RemoteRpcProvider(ServerImpl server, ClientImpl client) {
- this.server = server;
- this.client = client;
- }
-
- public void setBrokerSession(ProviderSession session) {
- server.setBrokerSession(session);
- }
-// public void setServerPool(ExecutorService serverPool) {
-// server.setServerPool(serverPool);
-// }
- public void start() {
- //when listener was being invoked and addRPCImplementation was being
- //called the client was null.
- server.setClient(client);
- server.start();
- client.start();
+ public void onRpcImplementationAdded(QName rpc) {
+
+ _logger.debug("Adding registration for [{}]", rpc);
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl();
+ routeId.setType(rpc);
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+ try {
+ routingTable.addGlobalRoute(routeId, server.getServerAddress());
+ _logger.debug("Route added [{}-{}]", routeId, server.getServerAddress());
+
+ } catch (RoutingTableException | SystemException e) {
+ //TODO: This can be thrown when route already exists in the table. Broker
+ //needs to handle this.
+ _logger.error("Unhandled exception while adding global route to routing table [{}]", e);
+
+ }
}
-
@Override
- public Collection<ProviderFunctionality> getProviderFunctionality() {
- // TODO Auto-generated method stub
- return null;
+ public void onRpcImplementationRemoved(QName rpc) {
+
+ _logger.debug("Removing registration for [{}]", rpc);
+ RouteIdentifierImpl routeId = new RouteIdentifierImpl();
+ routeId.setType(rpc);
+
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+
+ try {
+ routingTable.removeGlobalRoute(routeId);
+ } catch (RoutingTableException | SystemException e) {
+ _logger.error("Route delete failed {}", e);
+ }
}
-
-
+ }
+
+ /**
+ * Listener for Routed Rpc registrations in broker
+ */
+ private class RoutedRpcListener
+ implements RouteChangeListener<RpcRoutingContext, InstanceIdentifier> {
+
+ /**
+ *
+ * @param routeChange
+ */
@Override
- public void onSessionInitiated(ProviderSession session) {
- server.setBrokerSession(session);
- start();
+ public void onRouteChange(RouteChange<RpcRoutingContext, InstanceIdentifier> routeChange) {
+ Map<RpcRoutingContext, Set<InstanceIdentifier>> announcements = routeChange.getAnnouncements();
+ announce(getRouteIdentifiers(announcements));
+
+ Map<RpcRoutingContext, Set<InstanceIdentifier>> removals = routeChange.getRemovals();
+ remove(getRouteIdentifiers(removals));
+ }
+
+ /**
+ *
+ * @param announcements
+ */
+ private void announce(Set<RpcRouter.RouteIdentifier> announcements) {
+ _logger.debug("Announcing [{}]", announcements);
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+ try {
+ routingTable.addRoutes(announcements, server.getServerAddress());
+ } catch (RoutingTableException | SystemException e) {
+ _logger.error("Route announcement failed {}", e);
+ }
}
-
-
- public void close() throws Exception {
- server.close();
- client.close();
+
+ /**
+ *
+ * @param removals
+ */
+ private void remove(Set<RpcRouter.RouteIdentifier> removals){
+ _logger.debug("Removing [{}]", removals);
+ RoutingTable<RpcRouter.RouteIdentifier, String> routingTable = getRoutingTable();
+ try {
+ routingTable.removeRoutes(removals, server.getServerAddress());
+ } catch (RoutingTableException | SystemException e) {
+ _logger.error("Route removal failed {}", e);
+ }
}
- @Override
- public void stop() {
- server.stop();
- client.stop();
+ /**
+ *
+ * @param changes
+ * @return
+ */
+ private Set<RpcRouter.RouteIdentifier> getRouteIdentifiers(Map<RpcRoutingContext, Set<InstanceIdentifier>> changes) {
+ RouteIdentifierImpl routeId = null;
+ Set<RpcRouter.RouteIdentifier> routeIdSet = new HashSet<RpcRouter.RouteIdentifier>();
+
+ for (RpcRoutingContext context : changes.keySet()){
+ routeId = new RouteIdentifierImpl();
+ routeId.setType(context.getRpc());
+ routeId.setContext(context.getContext());
+
+ for (InstanceIdentifier instanceId : changes.get(context)){
+ routeId.setRoute(instanceId);
+ routeIdSet.add(routeId);
+ }
+ }
+ return routeIdSet;
}
+
+
+
+ }
+
}
package org.opendaylight.controller.sal.connector.remoterpc;
import com.google.common.base.Optional;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
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.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.connector.remoterpc.impl.RoutingTableImpl;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
private RoutingTableImpl routingTableImpl = null;
- final private RouteChangeListener routeChangeListener;
+ //final private RouteChangeListener routeChangeListener;
- public RoutingTableProvider(BundleContext ctx,RouteChangeListener rcl) {
+ public RoutingTableProvider(BundleContext ctx){//,RouteChangeListener rcl) {
@SuppressWarnings("rawtypes")
ServiceTracker<RoutingTable, RoutingTable> rawTracker = new ServiceTracker<>(ctx, RoutingTable.class, null);
tracker = rawTracker;
tracker.open();
- routeChangeListener = rcl;
+ //routeChangeListener = rcl;
}
- public Optional<RoutingTable<String, String>> getRoutingTable() {
+ public Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> getRoutingTable() {
@SuppressWarnings("unchecked")
- RoutingTable<String,String> tracked = tracker.getService();
+ RoutingTable<RpcRouter.RouteIdentifier,String> tracked = tracker.getService();
if(tracked instanceof RoutingTableImpl){
if(routingTableImpl != tracked){
routingTableImpl= (RoutingTableImpl)tracked;
- routingTableImpl.setRouteChangeListener(routeChangeListener);
+ //routingTableImpl.setRouteChangeListener(routeChangeListener);
}
}
import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import org.opendaylight.yangtools.yang.common.QName;
import static com.google.common.base.Preconditions.checkState;
/**
- * ZeroMq based implementation of RpcRouter. It implements RouteChangeListener of RoutingTable
- * so that it gets route change notifications from routing table.
+ * ZeroMq based implementation of RpcRouter.
*/
-public class ServerImpl implements RemoteRpcServer, RouteChangeListener<String, String> {
+public class ServerImpl implements RemoteRpcServer {
private Logger _logger = LoggerFactory.getLogger(ServerImpl.class);
private ProviderSession brokerSession;
private ZMQ.Context context;
- private final RpcListener listener = new RpcListener();
-
private final String HANDLER_INPROC_ADDRESS = "inproc://rpc-request-handler";
private final int HANDLER_WORKER_COUNT = 2;
private final int HWM = 200;//high water mark on sockets
private String serverAddress;
private int port;
- private ClientImpl client;
-
- private RoutingTableProvider routingTableProvider;
-
public static enum State {
STARTING, STARTED, STOPPED;
}
this.port = port;
}
- public RoutingTableProvider getRoutingTableProvider() {
- return routingTableProvider;
- }
-
- public void setRoutingTableProvider(RoutingTableProvider routingTableProvider) {
- this.routingTableProvider = routingTableProvider;
- }
-
- public ClientImpl getClient(){
- return this.client;
- }
-
- public void setClient(ClientImpl client) {
- this.client = client;
- }
-
public State getStatus() {
return this.status;
}
remoteServices = new HashSet<QName>();//
serverPool = Executors.newSingleThreadExecutor();//main server thread
serverPool.execute(receive()); // Start listening rpc requests
- brokerSession.addRpcRegistrationListener(listener);
-
- announceLocalRpcs();
-
- registerRemoteRpcs();
status = State.STARTED;
_logger.info("Remote RPC Server started [{}]", getServerAddress());
if (State.STOPPED == this.getStatus()) return; //do nothing
- unregisterLocalRpcs();
-
if (serverPool != null)
serverPool.shutdown();
/**
* Closes ZMQ Context. It tries to gracefully terminate the context. If
- * termination takes more than a second, its forcefully shutdown.
+ * termination takes more than 5 seconds, its forcefully shutdown.
*/
private void closeZmqContext() {
ExecutorService exec = Executors.newSingleThreadExecutor();
};
}
- /**
- * Register the remote RPCs from the routing table into broker
- */
- private void registerRemoteRpcs(){
- Optional<RoutingTable<String, String>> routingTableOptional = routingTableProvider.getRoutingTable();
-
- Preconditions.checkState(routingTableOptional.isPresent(), "Routing table is absent");
-
- Set<Map.Entry> remoteRoutes =
- routingTableProvider.getRoutingTable().get().getAllRoutes();
-
- //filter out all entries that contains local address
- //we dont want to register local RPCs as remote
- Predicate<Map.Entry> notLocalAddressFilter = new Predicate<Map.Entry>(){
- public boolean apply(Map.Entry remoteRoute){
- return !getServerAddress().equalsIgnoreCase((String)remoteRoute.getValue());
- }
- };
-
- //filter the entries created by current node
- Set<Map.Entry> filteredRemoteRoutes = Sets.filter(remoteRoutes, notLocalAddressFilter);
-
- for (Map.Entry route : filteredRemoteRoutes){
- onRouteUpdated((String) route.getKey(), "");//value is not needed by broker
- }
- }
-
- /**
- * Un-Register the local RPCs from the routing table
- */
- private void unregisterLocalRpcs(){
- Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
- for (QName rpc : currentlySupported) {
- listener.onRpcImplementationRemoved(rpc);
- }
- }
-
- /**
- * Publish all the locally registered RPCs in the routing table
- */
- private void announceLocalRpcs(){
- Set<QName> currentlySupported = brokerSession.getSupportedRpcs();
- for (QName rpc : currentlySupported) {
- listener.onRpcImplementationAdded(rpc);
- }
- }
-
- /**
- * @param key
- * @param value
- */
- @Override
- public void onRouteUpdated(String key, String value) {
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- try {
- _logger.debug("Updating key/value {}-{}", key, value);
- brokerSession.addRpcImplementation(
- (QName) rId.fromString(key).getType(), client);
-
- //TODO: Check with Tony for routed rpc
- //brokerSession.addRoutedRpcImplementation((QName) rId.fromString(key).getRoute(), client);
- } catch (Exception e) {
- _logger.info("Route update failed {}", e);
- }
- }
-
- /**
- * @param key
- */
- @Override
- public void onRouteDeleted(String key) {
- //TODO: Broker session needs to be updated to support this
- throw new UnsupportedOperationException();
- }
-
/**
* Finds IPv4 address of the local VM
* TODO: This method is non-deterministic. There may be more than one IPv4 address. Cant say which
return null;
}
- /**
- * Listener for rpc registrations
- */
- private class RpcListener implements RpcRegistrationListener {
-
- @Override
- public void onRpcImplementationAdded(QName name) {
-
- //if the service name exists in the set, this notice
- //has bounced back from the broker. It should be ignored
- if (remoteServices.contains(name))
- return;
-
- _logger.debug("Adding registration for [{}]", name);
- RouteIdentifierImpl routeId = new RouteIdentifierImpl();
- routeId.setType(name);
-
- RoutingTable<String, String> routingTable = getRoutingTable();
-
- try {
- routingTable.addGlobalRoute(routeId.toString(), getServerAddress());
- _logger.debug("Route added [{}-{}]", name, getServerAddress());
-
- } catch (RoutingTableException | SystemException e) {
- //TODO: This can be thrown when route already exists in the table. Broker
- //needs to handle this.
- _logger.error("Unhandled exception while adding global route to routing table [{}]", e);
-
- }
- }
-
- @Override
- public void onRpcImplementationRemoved(QName name) {
-
- _logger.debug("Removing registration for [{}]", name);
- RouteIdentifierImpl routeId = new RouteIdentifierImpl();
- routeId.setType(name);
-
- RoutingTable<String, String> routingTable = getRoutingTable();
-
- try {
- routingTable.removeGlobalRoute(routeId.toString());
- } catch (RoutingTableException | SystemException e) {
- _logger.error("Route delete failed {}", e);
- }
- }
-
- private RoutingTable<String, String> getRoutingTable(){
- Optional<RoutingTable<String, String>> routingTable =
- routingTableProvider.getRoutingTable();
-
- checkNotNull(routingTable.isPresent(), "Routing table is null");
-
- return routingTable.get();
- }
- }
-
- /*
- * Listener for Route changes in broker. Broker notifies this listener in the event
- * of any change (add/delete). Listener then updates the routing table.
- */
- private class BrokerRouteChangeListener
- implements org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener<RpcRoutingContext, InstanceIdentifier>{
-
- @Override
- public void onRouteChange(RouteChange<RpcRoutingContext, InstanceIdentifier> routeChange) {
-
- }
- }
-
}
public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>,Serializable {
- transient ObjectMapper mapper = new ObjectMapper();
-
private QName context;
private QName type;
private InstanceIdentifier route;
public void setRoute(InstanceIdentifier route) {
this.route = route;
}
-
- @Override
- public String toString() {
- try {
- return mapper.writeValueAsString(this);
- } catch (Throwable e) {
- //do nothing
- }
-
- return super.toString();
- }
-
- public RpcRouter.RouteIdentifier fromString(String input)
- throws Exception {
-
- JsonNode root = mapper.readTree(input);
- this.context = parseQName(root.get("context"));
- this.type = parseQName(root.get("type"));
-
- return this;
- }
-
- private QName parseQName(JsonNode node){
- if (node == null) return null;
-
- String namespace = (node.get("namespace") != null) ?
- node.get("namespace").asText() : "";
-
- String localName = (node.get("localName") != null) ?
- node.get("localName").asText() : "";
-
- URI uri = URI.create(namespace);
- return new QName(uri, localName);
- }
}
identity remote-zeromq-rpc-server {
base config:module-type;
- config:provided-service remote-rpc-server;
- config:provided-service remote-rpc-client;
config:java-name-prefix ZeroMQServer;
}
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
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;
//mock routing table
routingTableProvider = mock(RoutingTableProvider.class);
- RoutingTable<String, String> mockRoutingTable = new MockRoutingTable<String, String>();
- Optional<RoutingTable<String, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
+ RoutingTable<RpcRouter.RouteIdentifier, String> mockRoutingTable = new MockRoutingTable<String, String>();
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
when(routingTableProvider.getRoutingTable()).thenReturn(optionalRoutingTable);
//mock ClientRequestHandler
}
- @Test
+ //@Test
public void invokeRpc_NormalCall_ShouldReturnSuccess() throws Exception {
when(mockHandler.handle(any(Message.class))).
Assert.assertNull(result.getResult());
}
- @Test
+ //@Test
public void invokeRpc_HandlerThrowsException_ShouldReturnError() throws Exception {
when(mockHandler.handle(any(Message.class))).
}
+ @Override
+ public void addRoutes(Set set, Object o) throws RoutingTableException, SystemException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void removeRoutes(Set set, Object o) throws RoutingTableException, SystemException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
@Override
public void removeGlobalRoute(Object o) throws RoutingTableException, SystemException {
}
+ @Override
+ public Object getGlobalRoute(Object o) throws RoutingTableException, SystemException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
@Override
public Set getRoutes(Object o) {
Set<String> routes = new HashSet<String>();
}
@Override
- public Set<Map.Entry> getAllRoutes() {
- return Collections.emptySet();
+ public Object getLastAddedRoute(Object o) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
}
- @Override
- public Object getARoute(Object o) {
- return null;
- }
+// @Override
+// public Set<Map.Entry> getAllRoutes() {
+// return Collections.emptySet();
+// }
- @Override
- public void registerRouteChangeListener(RouteChangeListener routeChangeListener) {
-
- }
+// @Override
+// public Object getARoute(Object o) {
+// return null;
+// }
}
--- /dev/null
+package org.opendaylight.controller.sal.connector.remoterpc;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RemoteRpcProviderTest {
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testSetRoutingTableProvider() throws Exception {
+
+ }
+
+ @Test
+ public void testOnSessionInitiated() throws Exception {
+
+ }
+
+ @Test
+ public void testGetSupportedRpcs() throws Exception {
+
+ }
+
+ @Test
+ public void testGetProviderFunctionality() throws Exception {
+
+ }
+
+ @Test
+ public void testInvokeRpc() throws Exception {
+
+ }
+
+ @Test
+ public void testInvokeRoutedRpc() throws Exception {
+
+ }
+
+ @Test
+ public void testStart() throws Exception {
+
+ }
+
+ @Test
+ public void testClose() throws Exception {
+
+ }
+
+ @Test
+ public void testStop() throws Exception {
+
+ }
+}
+++ /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;
-
-import java.net.URI;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import org.junit.Assert;
-import org.junit.Test;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.RouteIdentifierImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RouteIdentifierImplTest {
-
- Logger _logger = LoggerFactory.getLogger(RouteIdentifierImplTest.class);
-
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
-
- @Test
- public void testToString() throws Exception {
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- rId.setType(QNAME);
-
- _logger.debug(rId.toString());
-
- Assert.assertTrue(true);
-
- }
-
- @Test
- public void testFromString() throws Exception {
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- rId.setType(QNAME);
-
- String s = rId.toString();
- _logger.debug("serialized route: {}", s);
-
- RpcRouter.RouteIdentifier ref = new RouteIdentifierImpl().fromString(s);
- _logger.debug("deserialized route: {}", ref);
-
- Assert.assertTrue(true);
- }
-
- @Test(expected = JsonParseException.class)
- public void testFromInvalidString() throws Exception {
- String invalidInput = "aklhdgadfa;;;;;;;]]]]=]ag" ;
- RouteIdentifierImpl rId = new RouteIdentifierImpl();
- rId.fromString(invalidInput);
-
- _logger.debug("" + rId);
- Assert.assertTrue(true);
- }
-}
import com.google.common.base.Optional;
import junit.framework.Assert;
import org.junit.*;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
import org.opendaylight.controller.sal.connector.remoterpc.utils.MessagingUtil;
import org.opendaylight.controller.sal.core.api.Broker;
server = new ServerImpl(port);
server.setBrokerSession(brokerSession);
- server.setRoutingTableProvider(routingTableProvider);
- RoutingTable<String, String> mockRoutingTable = new MockRoutingTable<String, String>();
- Optional<RoutingTable<String, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
+ RoutingTable<RpcRouter.RouteIdentifier, String> mockRoutingTable = new MockRoutingTable<String, String>();
+ Optional<RoutingTable<RpcRouter.RouteIdentifier, String>> optionalRoutingTable = Optional.fromNullable(mockRoutingTable);
when(routingTableProvider.getRoutingTable()).thenReturn(optionalRoutingTable);
when(brokerSession.addRpcRegistrationListener(listener)).thenReturn(null);
Assert.assertEquals(ServerImpl.State.STOPPED, server.getStatus());
}
- @Test
- public void getRoutingTableProvider_Call_ShouldReturnRoutingTable() throws Exception {
- Assert.assertNotNull(server.getRoutingTableProvider());
- }
-
@Test
public void getBrokerSession_Call_ShouldReturnBrokerSession() throws Exception {
Optional<Broker.ProviderSession> mayBeBroker = server.getBrokerSession();
</Export-Package>
<Import-Package>
com.sun.jersey.spi.container.servlet,
- org.codehaus.jackson.annotate,
+ !org.codehaus.jackson.annotate,
javax.ws.rs,
javax.ws.rs.core,
javax.xml.bind,
javax.xml.bind.annotation,
org.slf4j,
org.apache.catalina.filters,
- org.codehaus.jackson.jaxrs,
+ !org.codehaus.jackson.jaxrs,
org.opendaylight.controller.sample.zeromq.provider,
org.opendaylight.controller.sample.zeromq.consumer,
org.opendaylight.controller.sal.utils,
package org.opendaylight.controller.sal.restconf.binding.impl;
import java.net.URL;
-import java.util.concurrent.Future;
+
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.restconf.client.api.RestconfClientContext;
import org.opendaylight.yangtools.restconf.client.api.RestconfClientContextFactory;
import org.opendaylight.yangtools.restconf.client.api.UnsupportedProtocolException;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.DataRoot;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder;
import org.slf4j.Logger;
public DataBrokerServiceImpl(URL baseUrl, BindingIndependentMappingService mappingService, SchemaContextHolder schemaContextHolder) throws UnsupportedProtocolException {
this.restconfClientContext = restconfClientContextFactory.getRestconfClientContext(baseUrl, mappingService, schemaContextHolder);
}
- @Override
- public <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType) {
- return null;
- }
-
- @Override
- public <T extends DataRoot> T getData(DataStoreIdentifier store, T filter) {
- return null;
- }
-
- @Override
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType) {
- return null;
- }
-
- @Override
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter) {
- return null;
- }
-
- @Override
- public RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store, DataRoot changeSet) {
- return null;
- }
-
- @Override
- public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {
- return null;
- }
-
- @Override
- public DataObject getData(InstanceIdentifier<? extends DataObject> data) {
- return null;
- }
-
- @Override
- public DataObject getConfigurationData(InstanceIdentifier<?> data) {
- return null;
- }
@Override
public DataModificationTransaction beginTransaction() {
return null;
}
- @Override
- public void registerChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {
-
- }
-
- @Override
- public void unregisterChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {
-
- }
-
@Override
public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
//TODO implementation using restconf-client
//TODO implementation using restconf-client
return null;
}
-
-
-
-
-
}
* 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.sample.toaster.provider;
+package org.opendaylight.controller.sal.restconf.broker.client;
-public class ToasterActivator {
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+
+public interface SalRemoteClient extends AutoCloseable {
+
+ ConsumerContext registerConsumer();
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.restconf.broker.client;
+
+import java.net.URL;
+
+public class SalRemoteClientDeployer {
+
+ public static SalRemoteClient createSalRemoteClient(final URL url) {
+ return new SalRemoteClientImpl(url);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.restconf.broker.client;
+
+import java.net.URL;
+
+import javassist.ClassPool;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.sal.restconf.broker.SalRemoteServiceBroker;
+import org.opendaylight.yangtools.restconf.client.RestconfClientFactory;
+import org.opendaylight.yangtools.restconf.client.api.RestconfClientContext;
+import org.opendaylight.yangtools.restconf.client.api.UnsupportedProtocolException;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+class SalRemoteClientImpl implements SalRemoteClient {
+
+ private static final Logger logger = LoggerFactory.getLogger(SalRemoteClientImpl.class);
+
+ private final RestconfClientContext restconfClientContext;
+ private final SalRemoteServiceBroker salRemoteBroker;
+ private final RuntimeGeneratedMappingServiceImpl mappingService;
+
+ public SalRemoteClientImpl(final URL url) {
+ Preconditions.checkNotNull(url);
+
+ this.mappingService = new RuntimeGeneratedMappingServiceImpl();
+ this.mappingService.setPool(ClassPool.getDefault());
+ this.mappingService.init();
+
+ final ModuleInfoBackedContext moduleInfo = ModuleInfoBackedContext.create();
+ moduleInfo.addModuleInfos(BindingReflections.loadModuleInfos());
+ this.mappingService.onGlobalContextUpdated(moduleInfo.tryToCreateSchemaContext().get());
+
+ try {
+ this.restconfClientContext = new RestconfClientFactory().getRestconfClientContext(url, this.mappingService,
+ this.mappingService);
+
+ this.salRemoteBroker = new SalRemoteServiceBroker("remote-broker", restconfClientContext);
+ this.salRemoteBroker.start();
+ } catch (UnsupportedProtocolException e) {
+ logger.error("Unsupported protocol {}.", url.getProtocol(), e);
+ throw new IllegalArgumentException("Unsupported protocol.", e);
+ }
+ }
+
+ @Override
+ public ConsumerContext registerConsumer() {
+ return this.salRemoteBroker.registerConsumer(new BindingAwareConsumer() {
+
+ @Override
+ public void onSessionInitialized(ConsumerContext session) {
+ }
+ }, null);
+ }
+
+ @Override
+ public void close() throws Exception {
+ this.restconfClientContext.close();
+ this.salRemoteBroker.close();
+ }
+
+}
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.controller.sal.restconf.broker.listeners.RemoteDataChangeNotificationListener;
import org.opendaylight.controller.sal.restconf.broker.tools.RemoteStreamTools;
import org.opendaylight.controller.sal.restconf.broker.transactions.RemoteDataModificationTransaction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscriptionInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.CreateDataChangeEventSubscriptionOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.remote.rev140114.SalRemoteService;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.restconf.client.api.RestconfClientContext;
import org.opendaylight.yangtools.restconf.client.api.event.EventStreamInfo;
import org.opendaylight.yangtools.restconf.client.api.event.ListenableEventStreamContext;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.DataRoot;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
this.restconfClientContext = restconfClientContext;
this.salRemoteService = this.restconfClientContext.getRpcServiceContext(SalRemoteService.class).getRpcService();
}
- @Override
- public <T extends DataRoot> T getData(DataStoreIdentifier store, Class<T> rootType) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public <T extends DataRoot> T getData(DataStoreIdentifier store, T filter) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, Class<T> rootType) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public <T extends DataRoot> T getCandidateData(DataStoreIdentifier store, T filter) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store, DataRoot changeSet) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public DataObject getData(InstanceIdentifier<? extends DataObject> data) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public DataObject getConfigurationData(InstanceIdentifier<?> data) {
- throw new UnsupportedOperationException("Deprecated");
- }
@Override
public DataModificationTransaction beginTransaction() {
return remoteDataModificationTransaction;
}
- @Override
- public void registerChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
- @Override
- public void unregisterChangeListener(InstanceIdentifier<? extends DataObject> path, DataChangeListener changeListener) {
- throw new UnsupportedOperationException("Deprecated");
- }
-
@Override
public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
try {
final Map<String,EventStreamInfo> desiredEventStream = RemoteStreamTools.createEventStream(restconfClientContext,streamName);
ListenableEventStreamContext restConfListenableEventStreamContext = restconfClientContext.getEventStreamContext(desiredEventStream.get(streamName));
RemoteDataChangeNotificationListener remoteDataChangeNotificationListener = new RemoteDataChangeNotificationListener(listener);
- restConfListenableEventStreamContext.registerNotificationListener(remoteDataChangeNotificationListener);
- return new SalRemoteDataListenerRegistration(listener);
- }
-
- private class SalRemoteDataListenerRegistration implements ListenerRegistration<DataChangeListener> {
- private final DataChangeListener dataChangeListener;
- public SalRemoteDataListenerRegistration(DataChangeListener dataChangeListener){
- this.dataChangeListener = dataChangeListener;
- }
- @Override
- public DataChangeListener getInstance() {
- return this.dataChangeListener;
- }
- @Override
- public void close() {
- //noop
- }
+ final ListenerRegistration<?> reg = restConfListenableEventStreamContext.registerNotificationListener(remoteDataChangeNotificationListener);
+ return new AbstractListenerRegistration<DataChangeListener>(listener) {
+ @Override
+ protected void removeRegistration() {
+ reg.close();
+ }
+ };
}
}
+<?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>
<module>toaster</module>
<module>toaster-consumer</module>
<module>toaster-provider</module>
- </modules>
+ </modules>
<profiles>
<profile>
<activeByDefault>false</activeByDefault>
</activation>
<modules>
- <!--module>toaster-it</module -->
+ <module>toaster-it</module>
</modules>
</profile>
</profiles>
<groupId>org.opendaylight.controller.samples</groupId>
-</project>
+</project>
\ No newline at end of file
<tag>HEAD</tag>
</scm>
+ <properties>
+ <sal-binding-api.version>1.1-SNAPSHOT</sal-binding-api.version>
+ <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+
+ </properties>
+
<build>
<plugins>
<plugin>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Export-Package>org.opendaylight.controller.sample.toaster.provider.api</Export-Package>
- <Private-Package>org.opendaylight.controller.sample.toaster.provider.impl</Private-Package>
- <Bundle-Activator>org.opendaylight.controller.sample.toaster.provider.impl.ToastConsumerImpl</Bundle-Activator>
+ <Export-Package>
+ org.opendaylight.controller.sample.toaster.provider.api,
+ org.opendaylight.controller.config.yang.toaster-consumer,
+ </Export-Package>
+ <Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ </codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>
+ urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+ </namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${jmxGeneratorPath}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ <version>${sal-binding-api.version}</version>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-consumer-impl yang module local name: toaster-consumer-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:31:30 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_consumer.impl;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.controller.sample.toaster.provider.impl.ToastConsumerImpl;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDone;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+*
+*/
+public final class ToasterConsumerModule extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModule
+ {
+
+ public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ ToasterConsumerModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation(){
+ // No need to validate dependencies, since all dependencies have mandatory true flag in yang
+ // config-subsystem will perform the validation
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ ToasterService toasterService = getRpcRegistryDependency().getRpcService(ToasterService.class);
+
+ final ToastConsumerImpl consumer = new ToastConsumerImpl(toasterService);
+ final Registration<NotificationListener<ToastDone>> notificationRegistration = getNotificationServiceDependency()
+ .registerNotificationListener(ToastDone.class, consumer);
+
+ final ToasterConsumerRuntimeRegistration runtimeRegistration = getRootRuntimeBeanRegistratorWrapper().register(consumer);
+
+ final class AutoCloseableToastConsumer implements AutoCloseable, ToastConsumer {
+
+ @Override
+ public void close() throws Exception {
+ runtimeRegistration.close();
+ notificationRegistration.close();
+ }
+
+ @Override
+ public boolean createToast(Class<? extends ToastType> type, int doneness) {
+ return consumer.createToast(type, doneness);
+ }
+ }
+
+ return new AutoCloseableToastConsumer();
+ }
+}
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-consumer-impl yang module local name: toaster-consumer-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:31:30 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_consumer.impl;
+
+/**
+*
+*/
+public class ToasterConsumerModuleFactory extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModuleFactory
+{
+
+
+}
*/
package org.opendaylight.controller.sample.toaster.provider.impl;
-import java.util.Hashtable;
import java.util.concurrent.ExecutionException;
-import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
-import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.config.yang.config.toaster_consumer.impl.ToasterConsumerRuntimeMXBean;
import org.opendaylight.controller.sal.binding.api.NotificationListener;
-import org.opendaylight.controller.sal.binding.api.NotificationService;
import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInputBuilder;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDone;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.*;
import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ToastConsumerImpl extends AbstractBindingAwareConsumer implements BundleActivator, BindingAwareConsumer, ToastConsumer,
- NotificationListener<ToastDone> {
+public class ToastConsumerImpl implements
+ ToastConsumer,
+ NotificationListener<ToastDone>,ToasterConsumerRuntimeMXBean {
private static final Logger log = LoggerFactory.getLogger(ToastConsumerImpl.class);
private ToasterService toaster;
- private ConsumerContext session;
+ public ToastConsumerImpl(ToasterService toaster) {
+ this.toaster = toaster;
+ }
@Override
public boolean createToast(Class<? extends ToastType> type, int doneness) {
toastInput.setToasterToastType(type);
try {
- RpcResult<Void> result = getToastService().makeToast(toastInput.build()).get();
+ RpcResult<Void> result = toaster.makeToast(toastInput.build()).get();
if (result.isSuccessful()) {
- log.trace("Toast was successfuly finished");
+ log.trace("Toast was successfully finished");
} else {
- log.warn("Toast was not successfuly finished");
+ log.warn("Toast was not successfully finished");
}
return result.isSuccessful();
} catch (InterruptedException | ExecutionException e) {
- log.warn("Error occured during toast creation");
+ log.warn("Error occurred during toast creation");
}
return false;
}
- @Override
- @Deprecated
- protected void startImpl(BundleContext context) {
- context.registerService(ToastConsumer.class, this, new Hashtable<String,String>());
- }
-
- @Override
- public void onSessionInitialized(ConsumerContext session) {
- this.session = session;
- NotificationService notificationService = session.getSALService(NotificationService.class);
- notificationService.registerNotificationListener(ToastDone.class, this);
- }
-
@Override
public void onNotification(ToastDone notification) {
log.trace("ToastDone Notification Received: {} ",notification.getToastStatus());
-
}
- private ToasterService getToastService() {
- if (toaster == null) {
- toaster = session.getRpcService(ToasterService.class);
- }
- return toaster;
+ @Override
+ public Boolean makeHashBrownToast(Integer doneness) {
+ return createToast(HashBrown.class, doneness);
}
-
}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-consumer-impl {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl";
+ prefix "toaster-consumer-impl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+ import toaster-consumer { prefix toaster-consumer; revision-date 2014-01-31; }
+ import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-consumer impl implementation.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service implementation
+ identity toaster-consumer-impl {
+ base config:module-type;
+ config:provided-service toaster-consumer:toaster-consumer;
+ config:java-name-prefix ToasterConsumer;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case toaster-consumer-impl {
+ when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
+
+ container rpc-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-rpc-registry;
+ }
+ }
+ }
+
+ container notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-notification-service;
+ }
+ }
+ }
+
+ }
+ }
+
+ augment "/config:modules/config:module/config:state" {
+ case toaster-consumer-impl {
+ when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
+ rpcx:rpc-context-instance "make-hash-brown-toast-rpc";
+ }
+ }
+
+ identity make-hash-brown-toast-rpc;
+
+ rpc make-hash-brown-toast {
+ input {
+ uses rpcx:rpc-context-ref {
+ refine context-instance {
+ rpcx:rpc-context-instance make-hash-brown-toast-rpc;
+ }
+ }
+ leaf doneness {
+ type uint16;
+ }
+ }
+ output {
+ leaf result {
+ type boolean;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-consumer {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer";
+ prefix "toaster-consumer";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-consumer services.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service
+ identity toaster-consumer {
+
+ base "config:service-type";
+
+ config:java-class "org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer";
+ }
+}
\ No newline at end of file
<parent>
<artifactId>sal-samples</artifactId>
<groupId>org.opendaylight.controller.samples</groupId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</parent>
<artifactId>sample-toaster-it</artifactId>
<scm>
<url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
</scm>
- <properties>
- <exam.version>3.0.0</exam.version>
- <url.version>1.5.0</url.version>
- </properties>
<build>
<plugins>
</execution>
</executions>
</plugin>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <configuration>
- <includes>org.opendaylight.controller.*</includes>
- </configuration>
- <executions>
- <execution>
- <id>pre-test</id>
- <goals>
- <goal>prepare-agent</goal>
- </goals>
- </execution>
- <execution>
- <id>post-test</id>
- <phase>test</phase>
- <goals>
- <goal>report</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
</plugins>
- <pluginManagement>
- <plugins>
- <!--This plugin's configuration is used to store Eclipse
- m2e settings only. It has no influence on the Maven build itself. -->
- <plugin>
- <groupId>org.eclipse.m2e</groupId>
- <artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
- <configuration>
- <lifecycleMappingMetadata>
- <pluginExecutions>
- <pluginExecution>
- <pluginExecutionFilter>
- <groupId>
- org.ops4j.pax.exam
- </groupId>
- <artifactId>
- maven-paxexam-plugin
- </artifactId>
- <versionRange>
- [1.2.4,)
- </versionRange>
- <goals>
- <goal>
- generate-depends-file
- </goal>
- </goals>
- </pluginExecutionFilter>
- <action>
- <ignore></ignore>
- </action>
- </pluginExecution>
- </pluginExecutions>
- </lifecycleMappingMetadata>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
</build>
<dependencies>
<dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.samples</groupId>
- <artifactId>sample-toaster</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-it</artifactId>
+ <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-consumer</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-provider</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>1.1-SNAPSHOT</version>
</dependency>
+
<dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-native</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
</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>
<version>${exam.version}</version>
<scope>test</scope>
</dependency>
-
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
- <version>0.2.3-SNAPSHOT</version>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-native</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<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>
</dependencies>
</project>
*/
package org.opendaylight.controller.sample.toaster.it;
+import static org.junit.Assert.assertEquals;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.*;
+import static org.ops4j.pax.exam.CoreOptions.*;
+
+import javax.inject.Inject;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.config.yang.config.toaster_consumer.impl.ToasterConsumerRuntimeMXBean;
+import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.HashBrown;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WhiteBread;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
-import org.osgi.framework.BundleContext;
-
-import javax.inject.Inject;
-
-import static org.junit.Assert.assertTrue;
-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.systemProperty;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.ops4j.pax.exam.util.Filter;
import org.ops4j.pax.exam.util.PathUtils;
+import java.lang.management.ManagementFactory;
+
@RunWith(PaxExam.class)
public class ToasterTest {
- 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";
-
-
- public static final String SAMPLE = "org.opendaylight.controller.samples";
+ @Inject
+ @Filter(timeout=60*1000)
+ ToastConsumer toastConsumer;
- @Test
- public void properInitialized() throws Exception {
+ @Configuration
+ public Option[] config() {
+ return options(systemProperty("osgi.console").value("2401"), mavenBundle("org.slf4j", "slf4j-api")
+ .versionAsInProject(), //
+ mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
+
+ systemProperty("logback.configurationFile").value(
+ "file:" + PathUtils.getBaseDir()
+ + "/src/test/resources/logback.xml"),
+ mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
+ mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
+ systemProperty("osgi.bundles.defaultStartLevel").value("4"),
- Thread.sleep(500); // Waiting for services to get wired.
+ toasterBundles(),
+ mdSalCoreBundles(),
- assertTrue(consumer.createToast(WhiteBread.class, 5));
+ bindingAwareSalBundles(),
+ configMinumumBundles(),
+ // BASE Models
+ baseModelBundles(),
+ flowCapableModelBundles(),
+ // Set fail if unresolved bundle present
+ systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+ junitAndMockitoBundles());
}
- @Inject
- BindingAwareBroker broker;
+ private Option toasterBundles() {
+ return new DefaultCompositeOption(
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster-provider").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster-consumer").versionAsInProject(),
+ mavenBundle("org.opendaylight.controller.samples", "sample-toaster").versionAsInProject()
+ );
+ }
- @Inject
- ToastConsumer consumer;
+ @Test
+ public void testToaster() throws Exception {
- @Inject
- BundleContext ctx;
+ MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+ ObjectName consumerOn = new ObjectName("org.opendaylight.controller:instanceName=toaster-consumer-impl,type=RuntimeBean,moduleFactoryName=toaster-consumer-impl");
+ ObjectName providerOn = new ObjectName("org.opendaylight.controller:instanceName=toaster-provider-impl,type=RuntimeBean,moduleFactoryName=toaster-provider-impl");
- @Configuration
- public Option[] config() {
- return options(systemProperty("osgi.console").value("2401"),
- systemProperty("logback.configurationFile").value(
- "file:" + PathUtils.getBaseDir()
- + "/src/test/resources/logback.xml"),
- 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(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(SAMPLE, "sample-toaster").versionAsInProject(), //
- mavenBundle(SAMPLE, "sample-toaster-consumer").versionAsInProject(), //
- mavenBundle(SAMPLE, "sample-toaster-provider").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()
- );
+ long toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
+ assertEquals(0, toastsMade);
+
+ boolean toasts = true;
+
+ // Make toasts using OSGi service
+ toasts &= toastConsumer.createToast(HashBrown.class, 4);
+ toasts &= toastConsumer.createToast(WhiteBread.class, 8);
+
+ // Make toast using JMX/config-subsystem
+ toasts &= (Boolean)platformMBeanServer.invoke(consumerOn, "makeHashBrownToast", new Object[]{4}, new String[]{Integer.class.getName()});
+
+ Assert.assertTrue("Not all toasts done by " + toastConsumer, toasts);
+
+ // Verify toasts made count on provider via JMX/config-subsystem
+ toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
+ assertEquals(3, toastsMade);
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<persisted-snapshots>
+ <snapshots>
+ <snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27</capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
+ </capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
+ </capability>
+ <capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
+ <capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04</capability>
+ <capability>urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
+ </capability>
+ <capability>urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
+ </capability>
+ <capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
+ <capability>urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
+ </capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16</capability>
+ <capability>urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
+ </capability>
+ <capability>http://netconfcentral.org/ns/toaster?module=toaster&revision=2009-11-20</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer?module=toaster-consumer&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl?module=toaster-consumer-impl&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider?module=toaster-provider&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31</capability>
+
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ prefix:toaster-provider-impl
+ </type>
+ <name>toaster-provider-impl</name>
+
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+
+ <notification-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl">
+ prefix:toaster-consumer-impl
+ </type>
+ <name>toaster-consumer-impl</name>
+
+ <rpc-registry>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+
+ <notification-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:schema-service-singleton
+ </type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:hash-map-data-store
+ </type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:dom-broker-impl
+ </type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-data-store
+ </type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-broker-impl
+ </type>
+ <name>binding-broker-impl</name>
+ <notification-service
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-data-broker
+ </type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:runtime-generated-mapping
+ </type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-notification-broker
+ </type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-data-broker
+ </type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-broker-osgi-registry
+ </type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ binding:binding-dom-mapping-service
+ </type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:schema-service
+ </type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>
+ /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-notification-service
+ </type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>
+ /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-data-store
+ </type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>
+ /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
+ <instance>
+ <name>binding-rpc-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-broker-osgi-registry
+ </type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>
+ /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ binding-impl:binding-dom-mapping-service
+ </type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>
+ /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-broker-osgi-registry
+ </type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-data-broker
+ </type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>
+ /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+
+ </configuration>
+ </snapshot>
+
+ </snapshots>
+</persisted-snapshots>
</encoder>
</appender>
- <root level="error">
+
+ <logger name="org.opendaylight.yangtools.yang.parser" level="ERROR"/>
+
+ <root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
</scm>
+ <properties>
+ <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+ <sal-binding-api.version>1.1-SNAPSHOT</sal-binding-api.version>
+ </properties>
+
<build>
<plugins>
<plugin>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Bundle-Activator>org.opendaylight.controller.sample.toaster.provider.ToasterProvider</Bundle-Activator>
+ <Export-Package>
+ org.opendaylight.controller.config.yang.toaster_provider,
+ </Export-Package>
+ <Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ </codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>
+ urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+ </namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${jmxGeneratorPath}</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-provider-impl yang module local name: toaster-provider-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:05:32 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_provider.impl;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sample.toaster.provider.OpendaylightToaster;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+
+/**
+*
+*/
+public final class ToasterProviderModule extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule
+ {
+
+ public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ ToasterProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ protected void customValidation() {
+ // No need to validate dependencies, since all dependencies have mandatory true flag in yang
+ // config-subsystem will perform the validation for dependencies
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ final OpendaylightToaster opendaylightToaster = new OpendaylightToaster();
+
+ // Register to md-sal
+ opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
+ final BindingAwareBroker.RpcRegistration<ToasterService> rpcRegistration = getRpcRegistryDependency()
+ .addRpcImplementation(ToasterService.class, opendaylightToaster);
+
+ // Register runtimeBean for toaster statistics via JMX
+ final ToasterProviderRuntimeRegistration runtimeReg = getRootRuntimeBeanRegistratorWrapper().register(
+ opendaylightToaster);
+
+ // Wrap toaster as AutoCloseable and close registrations to md-sal at
+ // close()
+ final class AutoCloseableToaster implements AutoCloseable, ToasterData {
+
+ @Override
+ public void close() throws Exception {
+ rpcRegistration.close();
+ runtimeReg.close();
+ }
+
+ @Override
+ public Toaster getToaster() {
+ return opendaylightToaster.getToaster();
+ }
+ }
+
+ return new AutoCloseableToaster();
+ }
+
+}
--- /dev/null
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-provider-impl yang module local name: toaster-provider-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:05:32 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.toaster_provider.impl;
+
+/**
+*
+*/
+public class ToasterProviderModuleFactory extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModuleFactory
+{
+
+
+}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
import org.opendaylight.controller.sal.common.util.Futures;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class OpendaylightToaster implements ToasterData, ToasterService {
+public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean {
private static final Logger log = LoggerFactory.getLogger(OpendaylightToaster.class);
log.trace("Toast: {} doneness: {}", toastType, toastDoneness);
}
+ private final AtomicLong toastsMade = new AtomicLong(0);
+
+ @Override
+ public Long getToastsMade() {
+ return toastsMade.get();
+ }
+
private class MakeToastTask implements Callable<RpcResult<Void>> {
final MakeToastInput toastRequest;
log.trace("Toast Done");
logToastInput(toastRequest);
currentTask = null;
+
+ toastsMade.incrementAndGet();
+
return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
}
}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sample.toaster.provider;
-
-import java.util.Collection;
-import java.util.Collections;
-
-import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ToasterProvider extends AbstractBindingAwareProvider {
- private static final Logger log = LoggerFactory.getLogger(ToasterProvider.class);
-
- private ProviderContext providerContext;
- private final OpendaylightToaster toaster;
-
- public ToasterProvider() {
- toaster = new OpendaylightToaster();
- }
-
- @Override
- public void onSessionInitiated(ProviderContext session) {
- log.info("Provider Session initialized");
-
- this.providerContext = session;
- toaster.setNotificationProvider(session.getSALService(NotificationProviderService.class));
- providerContext.addRpcImplementation(ToasterService.class, toaster);
- }
-
- @Override
- public Collection<? extends RpcService> getImplementations() {
- return Collections.emptySet();
- }
-
- @Override
- public Collection<? extends ProviderFunctionality> getFunctionality() {
- return Collections.emptySet();
- }
-}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-provider-impl {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl";
+ prefix "toaster-provider-impl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import toaster-provider { prefix toaster-provider; revision-date 2014-01-31; }
+ import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-provider impl implementation.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service implementation
+ identity toaster-provider-impl {
+ base config:module-type;
+ config:provided-service toaster-provider:toaster-provider;
+ config:java-name-prefix ToasterProvider;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case toaster-provider-impl {
+ when "/config:modules/config:module/config:type = 'toaster-provider-impl'";
+
+ container rpc-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-rpc-registry;
+ }
+ }
+ }
+
+ container notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity mdsal:binding-notification-service;
+ }
+ }
+ }
+
+ }
+ }
+
+ augment "/config:modules/config:module/config:state" {
+ case toaster-provider-impl {
+ when "/config:modules/config:module/config:type = 'toaster-provider-impl'";
+
+ leaf toasts-made {
+ type uint32;
+ }
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module toaster-provider {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider";
+ prefix "toaster-provider";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ toaster-provider services.";
+
+ revision "2014-01-31" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of a service
+ identity toaster-provider {
+
+ base "config:service-type";
+
+ config:java-class "org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData";
+ }
+}
\ No newline at end of file
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-maven-plugin</artifactId>
- <version>${yangtools.version}</version>
<executions>
<execution>
<goals>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>config-netconf-connector</artifactId>
<name>${project.artifactId}</name>
package org.opendaylight.controller.netconf.confignetconfconnector.operations;
-import java.util.HashMap;
-import java.util.Map;
-
+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.util.ConfigRegistryClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import java.util.HashMap;
+import java.util.Map;
+
public class Commit extends AbstractConfigNetconfOperation {
private static final Logger logger = LoggerFactory.getLogger(Commit.class);
try {
status = this.transactionProvider.commitTransaction();
} catch (final IllegalStateException e) {
+ // TODO Illegal state thrown when no transaction yet for user
+ // Throw other exception type, or create transaction automatically
logger.warn("Commit failed: ", e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo.put(ErrorTag.operation_failed.name(),
"Operation failed. Use 'get-config' or 'edit-config' before triggering 'commit' operation");
throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
ErrorSeverity.error, errorInfo);
- } catch (final NetconfDocumentedException e) {
- throw new NetconfDocumentedException(
- "Unable to retrieve config snapshot after commit for persister, details: " + e.getMessage(),
- ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error, e.getErrorInfo());
+ } catch (ValidationException e) {
+ throw NetconfDocumentedException.wrap(e);
+ } catch (ConflictingVersionException e) {
+ throw NetconfDocumentedException.wrap(e);
+
}
logger.trace("Datastore {} committed successfully: {}", Datastore.candidate, status);
package org.opendaylight.controller.netconf.confignetconfconnector.operations;
-import java.util.HashMap;
-import java.util.Map;
-
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import java.util.HashMap;
+import java.util.Map;
+
public class DiscardChanges extends AbstractConfigNetconfOperation {
public static final String DISCARD = "discard-changes";
try {
fromXml(xml);
} catch (final IllegalArgumentException e) {
+ //FIXME where can IllegalStateException be thrown?
logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
try {
this.transactionProvider.abortTransaction();
} catch (final IllegalStateException e) {
+ //FIXME where can IllegalStateException be thrown?
logger.warn("Abort failed: ", e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo
try {
checkXml(xml);
} catch (IllegalStateException e) {
+ //FIXME where can IllegalStateException be thrown? I see precondition that guards for programming bugs..
logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value of datastore attribute");
throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute,
ErrorSeverity.error, errorInfo);
} catch (final IllegalArgumentException e) {
+ // FIXME use checked exception if it has domain meaning
logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
transactionProvider.validateTransaction();
} catch (ValidationException e) {
logger.warn("Validation failed", e);
- final Map<String, String> errorInfo = new HashMap<>();
- errorInfo.put(ErrorTag.operation_failed.name(), "Validation failed");
- throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
- ErrorSeverity.error, errorInfo);
+ throw NetconfDocumentedException.wrap(e);
} catch (IllegalStateException e) {
logger.warn("Validation failed", e);
final Map<String, String> errorInfo = new HashMap<>();
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
-import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.util.ConfigTransactionClient;
EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
try {
set(configRegistryClient, editConfigExecution);
- } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
+
+ } catch (IllegalStateException e) {
+ //FIXME: when can IllegalStateException be thrown?
+ // JmxAttributeValidationException is wrapped in DynamicWritableWrapper with ValidationException
+ // ValidationException is not thrown until validate or commit is issued
logger.warn("Set phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
try {
test(configRegistryClient, editConfigExecution, editConfigExecution.getDefaultStrategy());
- } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
+ } catch (IllegalStateException | ValidationException e) {
+ //FIXME: when can IllegalStateException be thrown?
logger.warn("Test phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
final Map<String, String> errorInfo = new HashMap<>();
errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
}
private void test(ConfigRegistryClient configRegistryClient, EditConfigExecution execution,
- EditStrategyType editStrategyType) {
+ EditStrategyType editStrategyType) throws ValidationException {
ObjectName taON = transactionProvider.getTestTransaction();
try {
return identityNameToSchemaNode.containsKey(idName);
}
+ // FIXME method never used
public IdentitySchemaNode getIdentitySchemaNode(String idName) {
Preconditions.checkState(identityNameToSchemaNode.containsKey(idName), "No identity under name %s", idName);
return identityNameToSchemaNode.get(idName);
private static final Logger logger = LoggerFactory.getLogger(Activator.class);
private BundleContext context;
- ServiceRegistration osgiRegistration;
+ private ServiceRegistration osgiRegistration;
+ private ConfigRegistryLookupThread configRegistryLookup = null;
@Override
public void start(BundleContext context) throws Exception {
@Override
public void stop(BundleContext context) throws Exception {
+ if (configRegistryLookup != null) {
+ configRegistryLookup.interrupt();
+ }
}
@Override
public synchronized void onYangStoreAdded(YangStoreService yangStoreService) {
- checkState(osgiRegistration == null, "More than one onYangStoreAdded received");
- NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
- logger.debug("Registering into OSGi");
- osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory,
- new Hashtable<String, Object>());
+ checkState(configRegistryLookup == null, "More than one onYangStoreAdded received");
+ configRegistryLookup = new ConfigRegistryLookupThread(yangStoreService);
+ configRegistryLookup.start();
}
@Override
public synchronized void onYangStoreRemoved() {
- osgiRegistration.unregister();
+ configRegistryLookup.interrupt();
+ if (osgiRegistration != null) {
+ osgiRegistration.unregister();
+ }
osgiRegistration = null;
+ configRegistryLookup = null;
+ }
+
+ private class ConfigRegistryLookupThread extends Thread {
+ private final YangStoreService yangStoreService;
+
+ private ConfigRegistryLookupThread(YangStoreService yangStoreService) {
+ super("config-registry-lookup");
+ this.yangStoreService = yangStoreService;
+ }
+
+ @Override
+ public void run() {
+ NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
+ logger.debug("Registering into OSGi");
+ osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory,
+ new Hashtable<String, Object>());
+ }
}
}
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+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.util.ConfigRegistryClient;
import org.opendaylight.controller.config.util.ConfigTransactionClient;
-import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Commit and notification send must be atomic
*/
- public synchronized CommitStatus commitTransaction() throws NetconfDocumentedException {
+ public synchronized CommitStatus commitTransaction() throws ValidationException, ConflictingVersionException {
final Optional<ObjectName> maybeTaON = getTransaction();
Preconditions.checkState(maybeTaON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting);
ObjectName taON = maybeTaON.get();
// no clean up: user can reconfigure and recover this transaction
logger.warn("Transaction {} failed on {}", taON, validationException.toString());
throw validationException;
- } catch (Exception e) {
+ } catch (ConflictingVersionException e) {
logger.error("Exception while commit of {}, aborting transaction", taON, e);
// clean up
abortTransaction();
transactionClient.validateConfig();
}
- public void validateTestTransaction(ObjectName taON) {
+ public void validateTestTransaction(ObjectName taON) throws ValidationException {
ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
transactionClient.validateConfig();
}
package org.opendaylight.controller.netconf.confignetconfconnector;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.management.InstanceAlreadyExistsException;
-import javax.management.InstanceNotFoundException;
-import javax.management.ObjectName;
-import javax.xml.parsers.ParserConfigurationException;
-
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.matchers.JUnitMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
public class NetconfMappingTest extends AbstractConfigTest {
transactionProvider = new TransactionProvider(this.configRegistryClient, NETCONF_SESSION_ID);
}
- private ObjectName createModule(final String instanceName) throws InstanceAlreadyExistsException, InstanceNotFoundException, URISyntaxException {
+ private ObjectName createModule(final String instanceName) throws InstanceAlreadyExistsException, InstanceNotFoundException, URISyntaxException, ValidationException, ConflictingVersionException {
final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction();
final ObjectName on = transaction.createModule(this.factory.getImplementationName(), instanceName);
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
+import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.util.ConfigTransactionClient;
import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
}
@Test
- public void test() throws NetconfDocumentedException {
+ public void test() throws NetconfDocumentedException, ValidationException {
EditConfig edit = new EditConfig(yangStoreSnapshot, provider, configRegistry,
ValidateTest.NETCONF_SESSION_ID_FOR_REPORTING);
EditConfigStrategy editStrat = mock(EditConfigStrategy.class);
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>config-persister-impl</artifactId>
<name>${project.artifactId}</name>
</dependency>
<!-- test dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>mockito-configuration</artifactId>
package org.opendaylight.controller.netconf.persist.impl;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.concurrent.Immutable;
-
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
-import com.google.common.base.Preconditions;
-import io.netty.channel.EventLoopGroup;
+import javax.annotation.concurrent.Immutable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
@Immutable
public class ConfigPusher {
private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class);
- private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000;
- private static final int NETCONF_SEND_ATTEMPTS = 20;
-
- private final InetSocketAddress address;
- private final EventLoopGroup nettyThreadGroup;
-
- // Default timeout for netconf becoming stable
- public static final long DEFAULT_MAX_WAIT_FOR_CAPABILITIES_MILLIS = TimeUnit.MINUTES.toMillis(2);
- public static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 5000;
- private final int delayMillis = 5000;
- private final long maxWaitForCapabilitiesMillis;
- private final long connectionTimeoutMillis;
- public ConfigPusher(InetSocketAddress address, EventLoopGroup nettyThreadGroup) {
- this(address, nettyThreadGroup, DEFAULT_MAX_WAIT_FOR_CAPABILITIES_MILLIS, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
- }
+ private final ConfigPusherConfiguration configuration;
- public ConfigPusher(InetSocketAddress address, EventLoopGroup nettyThreadGroup,
- long maxWaitForCapabilitiesMillis, long connectionTimeoutMillis) {
- this.address = address;
- this.nettyThreadGroup = nettyThreadGroup;
- this.maxWaitForCapabilitiesMillis = maxWaitForCapabilitiesMillis;
- this.connectionTimeoutMillis = connectionTimeoutMillis;
+ public ConfigPusher(ConfigPusherConfiguration configuration) {
+ this.configuration = configuration;
}
public synchronized LinkedHashMap<ConfigSnapshotHolder, EditAndCommitResponseWithRetries> pushConfigs(
throws InterruptedException {
ConflictingVersionException lastException = null;
- int maxAttempts = 30;
+ int maxAttempts = configuration.netconfPushConfigAttempts;
for (int retryAttempt = 1; retryAttempt <= maxAttempts; retryAttempt++) {
NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities());
EditAndCommitResponse editAndCommitResponse = pushLastConfig(configSnapshotHolder, netconfClient);
return new EditAndCommitResponseWithRetries(editAndCommitResponse, retryAttempt);
} catch (ConflictingVersionException e) {
+ logger.debug("Conflicting version detected, will retry after timeout");
lastException = e;
- Thread.sleep(1000);
+ Thread.sleep(configuration.netconfPushConfigDelayMs);
} catch (RuntimeException e) {
throw new IllegalStateException("Unable to load " + configSnapshotHolder, e);
} finally {
// could be utilized by integration tests
final long pollingStartNanos = System.nanoTime();
- final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(maxWaitForCapabilitiesMillis);
+ final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(configuration.netconfCapabilitiesWaitTimeoutMs);
int attempt = 0;
- NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown", address.getAddress().getHostAddress(),
- Integer.toString(address.getPort()), "tcp", "persister");
+ NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown",
+ configuration.netconfAddress.getAddress().getHostAddress(),
+ Integer.toString(configuration.netconfAddress.getPort()), "tcp", "persister");
Set<String> latestCapabilities = null;
while (System.nanoTime() < deadlineNanos) {
attempt++;
- NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(nettyThreadGroup,
- nettyThreadGroup, additionalHeader, connectionTimeoutMillis);
+ NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(configuration.eventLoopGroup,
+ configuration.eventLoopGroup, additionalHeader, configuration.connectionAttemptTimeoutMs);
NetconfClient netconfClient;
try {
- netconfClient = new NetconfClient(this.toString(), address, delayMillis, netconfClientDispatcher);
+ netconfClient = new NetconfClient(this.toString(), configuration.netconfAddress, configuration.connectionAttemptDelayMs, netconfClientDispatcher);
} catch (IllegalStateException e) {
- logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
+ logger.debug("Netconf {} was not initialized or is not stable, attempt {}", configuration.netconfAddress, attempt, e);
netconfClientDispatcher.close();
- Thread.sleep(delayMillis);
+ Thread.sleep(configuration.connectionAttemptDelayMs);
continue;
}
latestCapabilities = netconfClient.getCapabilities();
logger.trace("Session id received from netconf server: {}", netconfClient.getClientSession());
return netconfClient;
}
- logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, latestCapabilities);
+ Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
+ logger.debug("Netconf server did not provide required capabilities. Attempt {}. " +
+ "Expected but not found: {}, all expected {}, current {}",
+ attempt, allNotFound, expectedCaps, latestCapabilities);
Util.closeClientAndDispatcher(netconfClient);
- Thread.sleep(delayMillis);
+ Thread.sleep(configuration.connectionAttemptDelayMs);
}
if (latestCapabilities == null) {
- logger.error("Could not connect to the server in {} ms", maxWaitForCapabilitiesMillis);
+ logger.error("Could not connect to the server in {} ms", configuration.netconfCapabilitiesWaitTimeoutMs);
throw new RuntimeException("Could not connect to netconf server");
}
- Set<String> allNotFound = new HashSet<>(expectedCaps);
- allNotFound.removeAll(latestCapabilities);
+ Set<String> allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities);
logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}",
allNotFound, expectedCaps, latestCapabilities);
throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound);
}
+ private static Set<String> computeNotFoundCapabilities(Set<String> expectedCaps, Set<String> latestCapabilities) {
+ Set<String> allNotFound = new HashSet<>(expectedCaps);
+ allNotFound.removeAll(latestCapabilities);
+ return allNotFound;
+ }
+
/**
* Sends two RPCs to the netconf server: edit-config and commit.
}
- private static NetconfMessage sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfClient netconfClient) throws IOException {
+ private NetconfMessage sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfClient netconfClient)
+ throws ConflictingVersionException, IOException {
try {
- NetconfMessage netconfMessage = netconfClient.sendMessage(request, NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
+ NetconfMessage netconfMessage = netconfClient.sendMessage(request,
+ configuration.netconfSendMessageMaxAttempts, configuration.netconfSendMessageDelayMs);
NetconfUtil.checkIsMessageOk(netconfMessage);
return netconfMessage;
- } catch (RuntimeException | ExecutionException | InterruptedException | TimeoutException e) {
+ } catch(ConflictingVersionException e) {
+ logger.trace("conflicting version detected: {}", e.toString());
+ throw e;
+ } catch (RuntimeException | ExecutionException | InterruptedException | TimeoutException e) { // TODO: change NetconfClient#sendMessage to throw checked exceptions
logger.debug("Error while executing netconf transaction {} to {}", request, netconfClient, e);
throw new IOException("Failed to execute netconf transaction", e);
}
}
+
// load editConfig.xml template, populate /rpc/edit-config/config with parameter
private static NetconfMessage createEditConfigMessage(Element dataElement) {
String editConfigResourcePath = "/netconfOp/editConfig.xml";
'}';
}
}
+
}
--- /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.netconf.persist.impl;
+
+import io.netty.channel.EventLoopGroup;
+
+import javax.annotation.concurrent.Immutable;
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Configuration properties for ConfigPusher. Contains delays and timeouts for netconf
+ * connection establishment, netconf capabilities stabilization and configuration push.
+ */
+@Immutable
+public final class ConfigPusherConfiguration {
+
+ public static final long DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
+ public static final int DEFAULT_CONNECTION_ATTEMPT_DELAY_MS = 5000;
+
+ public static final int DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS = 20;
+ public static final int DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS = 1000;
+
+ public static final long DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(2);
+
+ public static final int DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS = 30;
+ public static final long DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS = TimeUnit.MINUTES.toMillis(1);
+
+ final InetSocketAddress netconfAddress;
+ final EventLoopGroup eventLoopGroup;
+
+ /**
+ * Total time to wait for capability stabilization
+ */
+ final long netconfCapabilitiesWaitTimeoutMs;
+
+ /**
+ * Delay between message send attempts
+ */
+ final int netconfSendMessageDelayMs;
+ /**
+ * Total number attempts to send a message
+ */
+ final int netconfSendMessageMaxAttempts;
+
+ /**
+ * Delay between connection establishment attempts
+ */
+ final int connectionAttemptDelayMs;
+ /**
+ * Total number of attempts to perform connection establishment
+ */
+ final long connectionAttemptTimeoutMs;
+
+ /**
+ * Total number of attempts to push configuration to netconf
+ */
+ final int netconfPushConfigAttempts;
+ /**
+ * Delay between configuration push attempts
+ */
+ final long netconfPushConfigDelayMs;
+
+ ConfigPusherConfiguration(InetSocketAddress netconfAddress, long netconfCapabilitiesWaitTimeoutMs,
+ int netconfSendMessageDelayMs, int netconfSendMessageMaxAttempts, int connectionAttemptDelayMs,
+ long connectionAttemptTimeoutMs, EventLoopGroup eventLoopGroup, int netconfPushConfigAttempts,
+ long netconfPushConfigDelayMs) {
+ this.netconfAddress = netconfAddress;
+ this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
+ this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
+ this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
+ this.connectionAttemptDelayMs = connectionAttemptDelayMs;
+ this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
+ this.eventLoopGroup = eventLoopGroup;
+ this.netconfPushConfigAttempts = netconfPushConfigAttempts;
+ this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
+ }
+}
--- /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.netconf.persist.impl;
+
+import io.netty.channel.EventLoopGroup;
+
+import java.net.InetSocketAddress;
+
+public class ConfigPusherConfigurationBuilder {
+ InetSocketAddress netconfAddress;
+ EventLoopGroup eventLoopGroup;
+
+ long netconfCapabilitiesWaitTimeoutMs = ConfigPusherConfiguration.DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS;
+ int netconfSendMessageDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS;
+ int netconfSendMessageMaxAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS;
+ int connectionAttemptDelayMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_DELAY_MS;
+ long connectionAttemptTimeoutMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS;
+ int netconfPushConfigAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS;
+ long netconfPushConfigDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS;
+
+ private ConfigPusherConfigurationBuilder() {
+ }
+
+ public static ConfigPusherConfigurationBuilder aConfigPusherConfiguration() {
+ return new ConfigPusherConfigurationBuilder();
+ }
+
+ public ConfigPusherConfigurationBuilder withNetconfAddress(InetSocketAddress netconfAddress) {
+ this.netconfAddress = netconfAddress;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withNetconfCapabilitiesWaitTimeoutMs(long netconfCapabilitiesWaitTimeoutMs) {
+ this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withNetconfSendMessageDelayMs(int netconfSendMessageDelayMs) {
+ this.netconfSendMessageDelayMs = netconfSendMessageDelayMs;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withNetconfSendMessageMaxAttempts(int netconfSendMessageMaxAttempts) {
+ this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withConnectionAttemptDelayMs(int connectionAttemptDelayMs) {
+ this.connectionAttemptDelayMs = connectionAttemptDelayMs;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withConnectionAttemptTimeoutMs(long connectionAttemptTimeoutMs) {
+ this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withEventLoopGroup(EventLoopGroup eventLoopGroup) {
+ this.eventLoopGroup = eventLoopGroup;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withNetconfPushConfigAttempts(int netconfPushConfigAttempts) {
+ this.netconfPushConfigAttempts = netconfPushConfigAttempts;
+ return this;
+ }
+
+ public ConfigPusherConfigurationBuilder withNetconfPushConfigDelayMs(long netconfPushConfigDelayMs) {
+ this.netconfPushConfigDelayMs = netconfPushConfigDelayMs;
+ return this;
+ }
+
+ public ConfigPusherConfiguration build() {
+ ConfigPusherConfiguration configPusherConfiguration = new ConfigPusherConfiguration(netconfAddress,
+ netconfCapabilitiesWaitTimeoutMs, netconfSendMessageDelayMs, netconfSendMessageMaxAttempts,
+ connectionAttemptDelayMs, connectionAttemptTimeoutMs, eventLoopGroup, netconfPushConfigAttempts,
+ netconfPushConfigDelayMs);
+ return configPusherConfiguration;
+ }
+}
public static PersisterAggregator createFromProperties(PropertiesProviderBaseImpl propertiesProvider) {
List<PersisterWithConfiguration> persisterWithConfigurations = new ArrayList<>();
String prefixes = propertiesProvider.getProperty("active");
- if (prefixes.isEmpty() == false) {
+ if (prefixes!=null && prefixes.isEmpty() == false) {
String [] keys = prefixes.split(",");
for (String index: keys) {
persisterWithConfigurations.add(PersisterAggregator.loadConfiguration(index, propertiesProvider));
package org.opendaylight.controller.netconf.persist.impl.osgi;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
import org.opendaylight.controller.netconf.persist.impl.ConfigPusher;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.osgi.framework.BundleActivator;
import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
+import java.util.concurrent.ThreadFactory;
import java.util.regex.Pattern;
public class ConfigPersisterActivator implements BundleActivator {
private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
- private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
- private static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
+ public static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
- private static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS = "maxWaitForCapabilitiesMillis";
+ public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS = "maxWaitForCapabilitiesMillis";
public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister";
public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0";
+ private final MBeanServer platformMBeanServer;
+ private final Optional<ConfigPusherConfiguration> initialConfigForPusher;
private volatile ConfigPersisterNotificationHandler jmxNotificationHandler;
private Thread initializationThread;
+ private ThreadFactory initializationThreadFactory;
private EventLoopGroup nettyThreadGroup;
private PersisterAggregator persisterAggregator;
+ public ConfigPersisterActivator() {
+ this(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable initializationRunnable) {
+ return new Thread(initializationRunnable, "ConfigPersister-registrator");
+ }
+ }, ManagementFactory.getPlatformMBeanServer(), null);
+ }
+
+ @VisibleForTesting
+ protected ConfigPersisterActivator(ThreadFactory threadFactory, MBeanServer mBeanServer,
+ ConfigPusherConfiguration initialConfigForPusher) {
+ this.initializationThreadFactory = threadFactory;
+ this.platformMBeanServer = mBeanServer;
+ this.initialConfigForPusher = Optional.fromNullable(initialConfigForPusher);
+ }
+
@Override
public void start(final BundleContext context) throws Exception {
logger.debug("ConfigPersister starting");
PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context);
- String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX);
- String regex;
- if (regexProperty != null) {
- regex = regexProperty;
- } else {
- regex = DEFAULT_IGNORED_REGEX;
- }
-
- String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS);
- long maxWaitForCapabilitiesMillis;
- if (timeoutProperty == null) {
- maxWaitForCapabilitiesMillis = ConfigPusher.DEFAULT_MAX_WAIT_FOR_CAPABILITIES_MILLIS;
- } else {
- maxWaitForCapabilitiesMillis = Integer.valueOf(timeoutProperty);
- }
-
- final Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex);
- nettyThreadGroup = new NioEventLoopGroup();
+ final Pattern ignoredMissingCapabilityRegex = getIgnoredCapabilitiesProperty(propertiesProvider);
persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider);
- final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
- "Netconf is not configured, persister is not operational", true);
- final ConfigPusher configPusher = new ConfigPusher(address, nettyThreadGroup, maxWaitForCapabilitiesMillis,
- ConfigPusher.DEFAULT_CONNECTION_TIMEOUT_MILLIS);
+ final ConfigPusher configPusher = new ConfigPusher(getConfigurationForPusher(context, propertiesProvider));
// offload initialization to another thread in order to stop blocking activator
Runnable initializationRunnable = new Runnable() {
logger.info("Configuration Persister initialization completed.");
}
};
- initializationThread = new Thread(initializationRunnable, "ConfigPersister-registrator");
+
+ initializationThread = initializationThreadFactory.newThread(initializationRunnable);
initializationThread.start();
}
+ private Pattern getIgnoredCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
+ String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX);
+ String regex;
+ if (regexProperty != null) {
+ regex = regexProperty;
+ } else {
+ regex = DEFAULT_IGNORED_REGEX;
+ }
+ return Pattern.compile(regex);
+ }
+
+ private Optional<Long> getMaxWaitForCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) {
+ String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS);
+ return Optional.fromNullable(timeoutProperty == null ? null : Long.valueOf(timeoutProperty));
+ }
+
+ private ConfigPusherConfiguration getConfigurationForPusher(BundleContext context,
+ PropertiesProviderBaseImpl propertiesProvider) {
+
+ // If configuration was injected via constructor, use it
+ if(initialConfigForPusher.isPresent())
+ return initialConfigForPusher.get();
+
+ Optional<Long> maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesProperty(propertiesProvider);
+ final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
+ "Netconf is not configured, persister is not operational", true);
+
+ nettyThreadGroup = new NioEventLoopGroup();
+
+ ConfigPusherConfigurationBuilder configPusherConfigurationBuilder = ConfigPusherConfigurationBuilder.aConfigPusherConfiguration();
+
+ if(maxWaitForCapabilitiesMillis.isPresent())
+ configPusherConfigurationBuilder.withNetconfCapabilitiesWaitTimeoutMs(maxWaitForCapabilitiesMillis.get());
+
+ return configPusherConfigurationBuilder
+ .withEventLoopGroup(nettyThreadGroup)
+ .withNetconfAddress(address)
+ .build();
+ }
+
@Override
public void stop(BundleContext context) throws Exception {
initializationThread.interrupt();
if (jmxNotificationHandler != null) {
jmxNotificationHandler.close();
}
- nettyThreadGroup.shutdownGracefully();
+ if(nettyThreadGroup!=null)
+ nettyThreadGroup.shutdownGracefully();
persisterAggregator.close();
}
}
--- /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.netconf.persist.impl.osgi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeoutException;
+
+import javax.management.MBeanServer;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration;
+import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder;
+
+import com.google.common.collect.Lists;
+import io.netty.channel.nio.NioEventLoopGroup;
+
+public class ConfigPersisterTest {
+
+ private MockedBundleContext ctx;
+ private ConfigPersisterActivator configPersisterActivator;
+ private static final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+
+ private static final String NETCONF_ADDRESS = "localhost";
+ private static final String NETCONF_PORT = "18383";
+ private static NioEventLoopGroup eventLoopGroup;
+
+ private void setUpContextAndStartPersister(Thread.UncaughtExceptionHandler exHandler, String requiredCapability, ConfigPusherConfiguration configuration)
+ throws Exception {
+ MockedBundleContext.DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability;
+ ctx = new MockedBundleContext(NETCONF_ADDRESS, NETCONF_PORT);
+ configPersisterActivator = new ConfigPersisterActivator(getThreadFactory(exHandler), mBeanServer,
+ configuration);
+ configPersisterActivator.start(ctx.getBundleContext());
+ }
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ eventLoopGroup = new NioEventLoopGroup();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ configPersisterActivator.stop(ctx.getBundleContext());
+ }
+
+ @AfterClass
+ public static void closeNettyGroup() throws Exception {
+ eventLoopGroup.shutdownGracefully();
+ }
+
+ @Test
+ public void testPersisterNetconfNotStarting() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+
+ setUpContextAndStartPersister(handler, "cap2", getConfiguration(100, 100).build());
+
+ waitTestToFinish(2000);
+
+ handler.assertException("connect to netconf endpoint", RuntimeException.class,
+ "Could not connect to netconf server");
+ }
+
+ @Test
+ public void testPersisterNotAllCapabilitiesProvided() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+ ConfigPusherConfiguration cfg = getConfiguration(500, 1000)
+ .withNetconfCapabilitiesWaitTimeoutMs(1000).build();
+
+ setUpContextAndStartPersister(handler, "required-cap", cfg);
+
+ try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
+
+ waitTestToFinish(2500);
+
+ handler.assertException("retrieve required capabilities from netconf endpoint", RuntimeException.class,
+ "Expected but not found:[required-cap]");
+ }
+ }
+
+ @Test
+ public void testPersisterNoResponseFromNetconfAfterEdit() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+ ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
+
+ setUpContextAndStartPersister(handler, "cap1", cfg);
+
+ try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) {
+
+ waitTestToFinish(3000);
+
+ handler.assertException("receive response from netconf endpoint", IllegalStateException.class,
+ "Unable to load", TimeoutException.class,
+ null, 3);
+
+ assertEquals(1 + 2, endpoint.getReceivedMessages().size());
+ assertHelloMessage(endpoint.getReceivedMessages().get(1));
+ assertEditMessage(endpoint.getReceivedMessages().get(2));
+ }
+ }
+
+ private ConfigPusherConfiguration getConfigurationWithOnePushAttempt() {
+ return getConfiguration(500, 1000)
+ .withNetconfCapabilitiesWaitTimeoutMs(1000)
+ .withNetconfPushConfigAttempts(1)
+ .withNetconfPushConfigDelayMs(100)
+ .withNetconfSendMessageMaxAttempts(3)
+ .withNetconfSendMessageDelayMs(500).build();
+ }
+
+ @Test
+ public void testPersisterSuccessfulPush() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+ ConfigPusherConfiguration cfg = getConfigurationForSuccess();
+
+ setUpContextAndStartPersister(handler, "cap1", cfg);
+
+ try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
+ MockNetconfEndpoint.okMessage)) {
+
+ waitTestToFinish(4000);
+
+ handler.assertException("register as JMX listener", RuntimeException.class,
+ "Cannot register as JMX listener to netconf");
+
+ assertEquals(1 + 3, endpoint.getReceivedMessages().size());
+ assertCommitMessage(endpoint.getReceivedMessages().get(3));
+ }
+ }
+
+ private ConfigPusherConfiguration getConfigurationForSuccess() {
+ return getConfiguration(500, 1000)
+ .withNetconfCapabilitiesWaitTimeoutMs(1000)
+ .withNetconfPushConfigAttempts(3)
+ .withNetconfPushConfigDelayMs(100)
+ .withNetconfSendMessageMaxAttempts(3)
+ .withNetconfSendMessageDelayMs(500).build();
+ }
+
+ @Test
+ public void testPersisterConflictingVersionException() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+ ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt();
+
+ setUpContextAndStartPersister(handler, "cap1", cfg);
+
+ try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
+ MockNetconfEndpoint.conflictingVersionErrorMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier();) {
+
+ Thread.sleep(4000);
+
+ handler.assertException("register as JMX listener", IllegalStateException.class,
+ "Maximum attempt count has been reached for pushing", ConflictingVersionException.class, "Optimistic lock failed", 1);
+
+ assertEquals(1 + 3, endpoint.getReceivedMessages().size());
+ assertCommitMessage(endpoint.getReceivedMessages().get(3));
+ }
+ }
+
+ @Test
+ public void testPersisterConflictingVersionExceptionThenSuccess() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+ ConfigPusherConfiguration cfg = getConfigurationForSuccess();
+
+ setUpContextAndStartPersister(handler, "cap1", cfg);
+
+ MockNetconfEndpoint.MessageSequence conflictingMessageSequence = new MockNetconfEndpoint.MessageSequence(
+ MockNetconfEndpoint.okMessage, MockNetconfEndpoint.conflictingVersionErrorMessage);
+ MockNetconfEndpoint.MessageSequence okMessageSequence = new MockNetconfEndpoint.MessageSequence(
+ MockNetconfEndpoint.okMessage, MockNetconfEndpoint.okMessage);
+
+ try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1",
+ Lists.newArrayList(conflictingMessageSequence, okMessageSequence));
+ DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
+
+ Thread.sleep(4000);
+
+ handler.assertNoException();
+
+ assertEquals(1 + 3/*Hello + Edit + Commit*/ + 3/*Hello + Edit + Commit*/, endpoint.getReceivedMessages().size());
+ assertCommitMessage(endpoint.getReceivedMessages().get(6));
+ }
+ }
+
+ @Test
+ public void testPersisterSuccessfulPushAndSuccessfulJMXRegistration() throws Exception {
+ final TestingExceptionHandler handler = new TestingExceptionHandler();
+ ConfigPusherConfiguration cfg = getConfigurationForSuccess();
+
+ setUpContextAndStartPersister(handler, "cap1", cfg);
+
+ try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage,
+ MockNetconfEndpoint.okMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) {
+
+ Thread.sleep(2000);
+
+ handler.assertNoException();
+
+ assertEquals(1 + 3, endpoint.getReceivedMessages().size());
+ }
+ }
+
+ private ConfigPusherConfigurationBuilder getConfiguration(int connectionAttemptDelayMs, int connectionAttemptTimeoutMs) {
+ return ConfigPusherConfigurationBuilder.aConfigPusherConfiguration()
+ .withEventLoopGroup(eventLoopGroup)
+ .withConnectionAttemptDelayMs(connectionAttemptDelayMs)
+ .withConnectionAttemptTimeoutMs(connectionAttemptTimeoutMs)
+ .withNetconfCapabilitiesWaitTimeoutMs(44)
+ .withNetconfAddress(new InetSocketAddress(NETCONF_ADDRESS, Integer.valueOf(NETCONF_PORT)));
+ }
+
+ private void waitTestToFinish(int i) throws InterruptedException {
+ Thread.sleep(i);
+ }
+
+
+ private DefaultCommitNotificationProducer startJMXCommitNotifier() {
+ return new DefaultCommitNotificationProducer(mBeanServer);
+ }
+
+ private void assertEditMessage(String netconfMessage) {
+ assertThat(netconfMessage,
+ JUnitMatchers.containsString(MockedBundleContext.DummyAdapterWithInitialSnapshot.CONFIG_SNAPSHOT));
+ }
+
+ private void assertCommitMessage(String netconfMessage) {
+ assertThat(netconfMessage, JUnitMatchers.containsString("<commit"));
+ }
+
+ private void assertHelloMessage(String netconfMessage) {
+ assertThat(netconfMessage,
+ JUnitMatchers.containsString("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"));
+ assertThat(netconfMessage, JUnitMatchers.containsString("<capability>"));
+ }
+
+ private MockNetconfEndpoint startMockNetconfEndpoint(String capability, List<MockNetconfEndpoint.MessageSequence> messageSequences) {
+ // Add first empty sequence for testing connection created by config persister at startup
+ messageSequences.add(0, new MockNetconfEndpoint.MessageSequence(Collections.<String>emptyList()));
+ return new MockNetconfEndpoint(capability, NETCONF_PORT, messageSequences);
+ }
+
+ private MockNetconfEndpoint startMockNetconfEndpoint(String capability, String... messages) {
+ return startMockNetconfEndpoint(capability, Lists.newArrayList(new MockNetconfEndpoint.MessageSequence(messages)));
+ }
+
+ public ThreadFactory getThreadFactory(final Thread.UncaughtExceptionHandler exHandler) {
+ return new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r, "config-persister-testing-activator");
+ thread.setUncaughtExceptionHandler(exHandler);
+ return thread;
+ }
+ };
+ }
+}
--- /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.netconf.persist.impl.osgi;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+
+import com.google.common.collect.Lists;
+
+class MockNetconfEndpoint implements AutoCloseable {
+
+ public static final int READ_SOCKET_TIMEOUT = 3000;
+
+ public static final String MSG_SEPARATOR = "]]>]]>\n";
+
+ private final AtomicBoolean stopped = new AtomicBoolean(false);
+ private List<String> receivedMessages = Lists.newCopyOnWriteArrayList();
+ private Thread innerThread;
+
+ MockNetconfEndpoint(String capability, String netconfPort, List<MessageSequence> messageSequence) {
+ helloMessage = helloMessage.replace("capability_place_holder", capability);
+ start(netconfPort, messageSequence);
+ }
+
+ private String helloMessage = "<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<capabilities>\n" +
+ "<capability>capability_place_holder</capability>\n" +
+ "</capabilities>\n" +
+ "<session-id>1</session-id>\n" +
+ "</hello>\n" +
+ MSG_SEPARATOR;
+
+ public static String conflictingVersionErrorMessage;
+ static {
+ try {
+ conflictingVersionErrorMessage = XmlUtil.toString(XmlFileLoader
+ .xmlFileToDocument("netconfMessages/conflictingversion/conflictingVersionResponse.xml")) + MSG_SEPARATOR;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String okMessage = "<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
+ "<ok/>\n" +
+ "</rpc-reply>" +
+ MSG_SEPARATOR ;
+
+ private void start(final String port, final List<MessageSequence> messagesToSend) {
+ innerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ int clientCounter = 0;
+
+ while (stopped.get() == false) {
+ try (ServerSocket s = new ServerSocket(Integer.valueOf(port))) {
+ s.setSoTimeout(READ_SOCKET_TIMEOUT);
+
+ Socket clientSocket = s.accept();
+ clientCounter++;
+ clientSocket.setSoTimeout(READ_SOCKET_TIMEOUT);
+
+ PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
+ BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+
+ // Negotiate
+ sendMessage(out, helloMessage);
+ receiveMessage(in);
+
+ // Accept next message (edit-config)
+ receiveMessage(in);
+
+ for (String message : getMessageSequenceForClient(messagesToSend, clientCounter)) {
+ sendMessage(out, message);
+ receiveMessage(in);
+ }
+ } catch (SocketTimeoutException e) {
+ // No more activity on netconf endpoint, close
+ return;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private Iterable<? extends String> getMessageSequenceForClient(List<MessageSequence> messagesToSend,
+ int clientCounter) {
+ if (messagesToSend.size() <= clientCounter) {
+ return messagesToSend.get(messagesToSend.size() - 1).getMessages();
+ } else {
+ return messagesToSend.get(clientCounter - 1).getMessages();
+ }
+ }
+
+ private void receiveMessage(BufferedReader in) throws Exception {
+ String message = readMessage(in);
+ if(message == null || message.equals(""))
+ return;
+ receivedMessages.add(message);
+ }
+
+ private String readMessage(BufferedReader in) throws IOException {
+ int c;
+ StringBuilder b = new StringBuilder();
+
+ while((c = in.read()) != -1) {
+ b.append((char)c);
+ if(b.toString().endsWith("]]>]]>"))
+ break;
+ }
+
+ return b.toString();
+ }
+
+ private void sendMessage(PrintWriter out, String message) throws InterruptedException {
+ out.print(message);
+ out.flush();
+ }
+
+ });
+ innerThread.setName("Mocked-netconf-endpoint-inner-thread");
+ innerThread.start();
+ }
+
+ public List<String> getReceivedMessages() {
+ return receivedMessages;
+ }
+
+ public void close() throws IOException, InterruptedException {
+ stopped.set(true);
+ innerThread.join();
+ }
+
+ static class MessageSequence {
+ private List<String> messages;
+
+ MessageSequence(List<String> messages) {
+ this.messages = messages;
+ }
+
+ MessageSequence(String... messages) {
+ this(Lists.newArrayList(messages));
+ }
+
+ public Collection<String> getMessages() {
+ return messages;
+ }
+ }
+}
--- /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.netconf.persist.impl.osgi;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.config.persist.api.PropertiesProvider;
+import org.opendaylight.controller.netconf.persist.impl.DummyAdapter;
+import org.osgi.framework.BundleContext;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import static org.mockito.Mockito.doReturn;
+
+final class MockedBundleContext {
+
+ @Mock
+ private BundleContext context;
+
+ MockedBundleContext(String netconfAddress, String netconfPort) {
+ MockitoAnnotations.initMocks(this);
+ initContext(netconfAddress, netconfPort);
+ }
+
+ public BundleContext getBundleContext() {
+ return context;
+ }
+
+ private void initContext(String netconfAddress, String netconfPort) {
+ initProp(context, ConfigPersisterActivator.IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX, null);
+
+ initPropNoPrefix(context, "netconf.tcp.client.address", netconfAddress);
+ initPropNoPrefix(context, "netconf.tcp.client.port", netconfPort);
+
+ initProp(context, "active", "1");
+ initProp(context, "1." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX, DummyAdapterWithInitialSnapshot.class.getName());
+ initProp(context, "1." + "readonly", "false");
+ initProp(context, "1." + ".properties.fileStorage", "target/configuration-persister-test/initial/");
+
+ }
+
+ private void initProp(BundleContext context, String key, String value) {
+ initPropNoPrefix(context, ConfigPersisterActivator.NETCONF_CONFIG_PERSISTER + "." + key, value);
+ }
+
+ private void initPropNoPrefix(BundleContext context, String key, String value) {
+ doReturn(value).when(context).getProperty(key);
+ }
+
+ public static class DummyAdapterWithInitialSnapshot extends DummyAdapter {
+
+ public static final String CONFIG_SNAPSHOT = "config-snapshot";
+ public static String expectedCapability = "cap2";
+
+ @Override
+ public List<ConfigSnapshotHolder> loadLastConfigs() throws IOException {
+ return Lists.newArrayList(getConfigSnapshopt());
+ }
+
+ @Override
+ public Persister instantiate(PropertiesProvider propertiesProvider) {
+ return this;
+ }
+
+ public ConfigSnapshotHolder getConfigSnapshopt() {
+ return new ConfigSnapshotHolder() {
+ @Override
+ public String getConfigSnapshot() {
+ return "<data><" + CONFIG_SNAPSHOT + "/></data>";
+ }
+
+ @Override
+ public SortedSet<String> getCapabilities() {
+ TreeSet<String> strings = Sets.newTreeSet();
+ strings.add(expectedCapability);
+ return strings;
+ }
+
+ @Override
+ public String toString() {
+ return getConfigSnapshot();
+ }
+ };
+ }
+ }
+}
--- /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.netconf.persist.impl.osgi;
+
+import org.junit.matchers.JUnitMatchers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+final class TestingExceptionHandler implements Thread.UncaughtExceptionHandler {
+
+ private Throwable t;
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ this.t = e;
+ }
+
+ public void assertException(String failMessageSuffix, Class<? extends Exception> exType, String exMessageToContain) {
+ if(t == null) {
+ fail("Should fail to " + failMessageSuffix);
+ }
+ else {
+ assertException(t, exType, exMessageToContain);
+ }
+ }
+
+ public void assertNoException() {
+ assertNull("No exception expected but was " + t, t);
+ }
+
+ private void assertException(Throwable t, Class<? extends Exception> exType, String exMessageToContain) {
+ assertEquals("Expected exception of type " + exType + " but was " + t, exType, t.getClass());
+ if(exMessageToContain!=null) {
+ assertThat(t.getMessage(), JUnitMatchers.containsString(exMessageToContain));
+ }
+ }
+
+ public void assertException(String failMessageSuffix, Class<? extends Exception> exType,
+ String exMessageToContain, Class<? extends Exception> nestedExType, String nestedExMessageToContain,
+ int nestedExDepth) {
+ assertException(failMessageSuffix, exType, exMessageToContain);
+ assertNotNull("Expected nested exception in " + t, t.getCause());
+ assertException(getNestedException(t, nestedExDepth), nestedExType, nestedExMessageToContain);
+ }
+
+ private Throwable getNestedException(Throwable t, int nestedExDepth) {
+
+ int depth = 0;
+ while(t.getCause() != null) {
+ t = t.getCause();
+ depth++;
+ if(nestedExDepth == depth)
+ return t;
+ }
+ throw new IllegalArgumentException("Unable to get nested exception from " + t + " from depth " + nestedExDepth);
+ }
+}
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ietf-netconf-monitoring-extension</artifactId>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ietf-netconf-monitoring</artifactId>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>netconf-api</artifactId>
</Private-Package>
<Import-Package>
javax.management,
+ org.opendaylight.controller.config.api,
org.opendaylight.controller.config.api.jmx,
org.opendaylight.protocol.framework,
io.netty.channel,
package org.opendaylight.controller.netconf.api;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
/**
private static final long serialVersionUID = 1L;
+
+
public enum ErrorType {
transport, rpc, protocol, application;
this.errorInfo = errorInfo;
}
+ public static NetconfDocumentedException wrap(ValidationException e) throws NetconfDocumentedException {
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_failed.name(), "Validation failed");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+ }
+
+ public static NetconfDocumentedException wrap(ConflictingVersionException e) throws NetconfDocumentedException {
+ final Map<String, String> errorInfo = new HashMap<>();
+ errorInfo.put(ErrorTag.operation_failed.name(), "Optimistic lock failed");
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed,
+ ErrorSeverity.error, errorInfo);
+ }
+
public ErrorType getErrorType() {
return this.errorType;
}
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>netconf-client</artifactId>
<name>${project.artifactId}</name>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>netconf-impl</artifactId>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>netconf-it</artifactId>
<artifactId>yang-store-api</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>yang-test</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-api</artifactId>
<artifactId>netconf-monitoring</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-it</artifactId>
+ <version>${mdsal.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-mapping-api</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-test</artifactId>
+ <version>${config.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>yang-store-impl</artifactId>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>maven-paxexam-plugin</artifactId>
+ <version>1.2.4</version>
+ <executions>
+ <execution>
+ <id>generate-config</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
</project>
--- /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.netconf.it.pax;
+
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.baseModelBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.bindingAwareSalBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.configMinumumBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.flowCapableModelBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAndMockitoBundles;
+import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+import javax.inject.Inject;
+import javax.xml.parsers.ParserConfigurationException;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.nio.NioEventLoopGroup;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.junit.runner.RunWith;
+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.util.xml.XmlUtil;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.ops4j.pax.exam.util.Filter;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(PaxExam.class)
+public class IdentityRefNetconfTest {
+
+ public static final int CLIENT_CONNECTION_TIMEOUT_MILLIS = 5000;
+
+ // Wait for controller to start
+ @Inject
+ @Filter(timeout = 60 * 1000)
+ BindingAwareBroker broker;
+
+ @Configuration
+ public Option[] config() {
+ return options(
+ systemProperty("osgi.console").value("2401"),
+ systemProperty("osgi.bundles.defaultStartLevel").value("4"),
+ systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
+
+ testingModules(),
+ loggingModules(),
+ mdSalCoreBundles(),
+ bindingAwareSalBundles(), configMinumumBundles(), baseModelBundles(), flowCapableModelBundles(),
+ junitAndMockitoBundles());
+ }
+
+ private Option loggingModules() {
+ return new DefaultCompositeOption(
+ 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());
+ }
+
+ private Option testingModules() {
+ return new DefaultCompositeOption(
+ mavenBundle("org.opendaylight.controller", "yang-test").versionAsInProject());
+ }
+
+ private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 18383);
+
+ @Test
+ public void testIdRef() throws Exception {
+ Preconditions.checkNotNull(broker, "Controller not initialized");
+
+ NioEventLoopGroup nettyThreadgroup = new NioEventLoopGroup();
+ NetconfClientDispatcher clientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup,
+ CLIENT_CONNECTION_TIMEOUT_MILLIS);
+
+ NetconfMessage edit = xmlFileToNetconfMessage("netconfMessages/editConfig_identities.xml");
+ NetconfMessage commit = xmlFileToNetconfMessage("netconfMessages/commit.xml");
+ NetconfMessage getConfig = xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
+
+ try (NetconfClient netconfClient = new NetconfClient("client", tcpAddress, CLIENT_CONNECTION_TIMEOUT_MILLIS, clientDispatcher)) {
+ sendMessage(edit, netconfClient);
+ sendMessage(commit, netconfClient);
+ sendMessage(getConfig, netconfClient, "id-test",
+ "<afi xmlns:prefix=\"urn:opendaylight:params:xml:ns:yang:controller:config:test:types\">prefix:test-identity1</afi>",
+ "<afi xmlns:prefix=\"urn:opendaylight:params:xml:ns:yang:controller:config:test:types\">prefix:test-identity2</afi>",
+ "<safi xmlns:prefix=\"urn:opendaylight:params:xml:ns:yang:controller:config:test:types\">prefix:test-identity2</safi>",
+ "<safi xmlns:prefix=\"urn:opendaylight:params:xml:ns:yang:controller:config:test:types\">prefix:test-identity1</safi>");
+ }
+
+ clientDispatcher.close();
+ }
+
+ private void sendMessage(NetconfMessage edit, NetconfClient netconfClient, String... containingResponse)
+ throws ExecutionException, InterruptedException, TimeoutException {
+ NetconfMessage response = netconfClient.sendRequest(edit).get();
+ if (containingResponse == null) {
+ Assert.assertThat(XmlUtil.toString(response.getDocument()), JUnitMatchers.containsString("<ok/>"));
+ } else {
+ for (String resp : containingResponse) {
+ Assert.assertThat(XmlUtil.toString(response.getDocument()), JUnitMatchers.containsString(resp));
+ }
+ }
+ }
+
+ public static NetconfMessage xmlFileToNetconfMessage(final String fileName) throws IOException, SAXException,
+ ParserConfigurationException {
+ return new NetconfMessage(xmlFileToDocument(fileName));
+ }
+
+ public static Document xmlFileToDocument(final String fileName) throws IOException, SAXException,
+ ParserConfigurationException {
+ // TODO xml messages from netconf-util test-jar cannot be loaded here(in OSGi), since test jar is not a bundle
+ try (InputStream resourceAsStream = IdentityRefNetconfTest.class.getClassLoader().getResourceAsStream(fileName)) {
+ Preconditions.checkNotNull(resourceAsStream);
+ final Document doc = XmlUtil.readXmlToDocument(resourceAsStream);
+ return doc;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<persisted-snapshots>
+ <snapshots>
+ <snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:test:types?module=test-types&revision=2013-11-27</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:test:impl?module=config-test-impl&revision=2013-04-03</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:test?module=config-test&revision=2013-06-13</capability>
+ </required-capabilities>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test:impl">prefix:impl-identity-test</type>
+ <name>id-test</name>
+ <identities-container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity2</afi>
+ </identities-container>
+ <identities xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <safi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity2</safi>
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity1</afi>
+ </identities>
+ <identities xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ <safi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity1</safi>
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity2</afi>
+ </identities>
+ <afi xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl" xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity1</afi>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <name>ref_binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker</type>
+ <name>ref_binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <name>ref_dom-broker</name>
+ </dom-broker>
+ <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <name>ref_runtime-mapping-singleton</name>
+ </mapping-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:logback:config">prefix:logback</type>
+ <name>singleton</name>
+ <console-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <threshold-filter>DEBUG</threshold-filter>
+ <name>console</name>
+ <encoder-pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</encoder-pattern>
+ </console-appenders>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>DEBUG</level>
+ <logger-name>ROOT</logger-name>
+ <appenders>console</appenders>
+ </loggers>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+ <name>hash-map-data-store</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>dom-broker</name>
+ <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-data-store</type>
+ <name>ref_hash-map-data-store</name>
+ </data-store>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:schema-service</type>
+ <instance>
+ <name>ref_yang-schema-service</name>
+ <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-data-store</type>
+ <instance>
+ <name>ref_hash-map-data-store</name>
+ <provider>/modules/module[type='hash-map-data-store'][name='hash-map-data-store']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <instance>
+ <name>ref_dom-broker</name>
+ <provider>/modules/module[type='dom-broker-impl'][name='dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+ <instance>
+ <name>ref_id-test</name>
+ <provider>/modules/module[type='impl-identity-test'][name='id-test']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <instance>
+ <name>ref_runtime-mapping-singleton</name>
+ <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-consumer-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/modules/module[type='binding-data-broker'][name='binding-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-rpc-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+ <instance>
+ <name>ref_binding-broker-impl</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-subscription-service</type>
+ <instance>
+ <name>ref_binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker</type>
+ <instance>
+ <name>ref_binding-data-broker</name>
+ <provider>/modules/module[type='binding-data-broker'][name='binding-data-broker']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+ </configuration>
+ </snapshot>
+ </snapshots>
+</persisted-snapshots>
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+ <commit></commit>
+</rpc>
--- /dev/null
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+<edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+ test-impl:impl-identity-test
+ </type>
+ <name>id-test</name>
+ <identities>
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity1</afi>
+ <safi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity2</safi>
+ </identities>
+ <identities>
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity2</afi>
+ <safi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity1</safi>
+ </identities>
+ <identities-container>
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity2</afi>
+ </identities-container>
+ <afi xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:test:types">prefix:test-identity1</afi>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ </services>
+ </config>
+</edit-config>
+</rpc>
--- /dev/null
+<rpc id="a" a="64" xmlnx="a:b:c:d" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="101">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ </get-config>
+</rpc>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>netconf-mapping-api</artifactId>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>netconf-monitoring</artifactId>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.controller</groupId>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
</parent>
<artifactId>netconf-util</artifactId>
<name>${project.artifactId}</name>
public static Document xmlFileToDocument(final String fileName) throws IOException, SAXException,
ParserConfigurationException {
try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) {
- Preconditions.checkNotNull(resourceAsStream);
+ Preconditions.checkNotNull(resourceAsStream, fileName);
final Document doc = XmlUtil.readXmlToDocument(resourceAsStream);
return doc;
}
<relativePath>../commons/opendaylight</relativePath>
</parent>
- <version>0.2.4-SNAPSHOT</version>
+ <version>0.2.5-SNAPSHOT</version>
<artifactId>netconf-subsystem</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<maven.bundle.version>2.4.0</maven.bundle.version>
<slf4j.version>1.7.2</slf4j.version>
<netconf.netty.version>4.0.10.Final</netconf.netty.version>
- <netconf.version>0.2.4-SNAPSHOT</netconf.version>
- <config.version>0.2.4-SNAPSHOT</config.version>
<salGeneratorPath>${project.build.directory}/generated-sources/sal</salGeneratorPath>
</properties>
import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
import org.opendaylight.controller.networkconfig.neutron.Neutron_IPs;
import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.utils.ServiceHelper;
/**
- * Open DOVE Northbound REST APIs.<br>
- * This class provides REST APIs for managing the open DOVE
+ * Neutron Northbound REST APIs.<br>
+ * This class provides REST APIs for managing Neutron Floating IPs
*
* <br>
* <br>
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
if (!floatingIPInterface.floatingIPExists(floatingipUUID))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
if (fields.size() > 0) {
NeutronFloatingIP ans = floatingIPInterface.getFloatingIP(floatingipUUID);
return Response.status(200).entity(
NeutronFloatingIP singleton = input.getSingleton();
// check existence of id in cache and return badrequest if exists
if (floatingIPInterface.floatingIPExists(singleton.getID()))
- return Response.status(400).build();
+ throw new BadRequestException("Floating IP UUID already exists.");
// check if the external network is specified, exists, and is an external network
String externalNetworkUUID = singleton.getFloatingNetworkUUID();
if (externalNetworkUUID == null)
- return Response.status(400).build();
+ throw new BadRequestException("external network UUID doesn't exist.");
if (!networkInterface.networkExists(externalNetworkUUID))
- return Response.status(400).build();
+ throw new BadRequestException("external network UUID doesn't exist.");
NeutronNetwork externNetwork = networkInterface.getNetwork(externalNetworkUUID);
if (!externNetwork.isRouterExternal())
- return Response.status(400).build();
+ throw new BadRequestException("external network isn't marked router:external");
// if floating IP is specified, make sure it can come from the network
String floatingIP = singleton.getFloatingIPAddress();
if (floatingIP != null) {
if (externNetwork.getSubnets().size() > 1)
- return Response.status(400).build();
+ throw new BadRequestException("external network doesn't have a subnet");
NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
if (!externSubnet.isValidIP(floatingIP))
- return Response.status(400).build();
+ throw new BadRequestException("external IP isn't valid for the specified subnet.");
if (externSubnet.isIPInUse(floatingIP))
- return Response.status(409).build();
+ throw new ResourceConflictException("floating IP is in use.");
}
// if port_id is specified, then check that the port exists and has at least one IP
String port_id = singleton.getPortUUID();
if (port_id != null) {
String fixedIP = null; // used for the fixedIP calculation
if (!portInterface.portExists(port_id))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Port UUID doesn't exist.");
NeutronPort port = portInterface.getPort(port_id);
if (port.getFixedIPs().size() < 1)
- return Response.status(400).build();
+ throw new BadRequestException("port UUID doesn't have an IP address.");
// if there is more than one fixed IP then check for fixed_ip_address
// and that it is in the list of port addresses
if (port.getFixedIPs().size() > 1) {
fixedIP = singleton.getFixedIPAddress();
if (fixedIP == null)
- return Response.status(400).build();
+ throw new BadRequestException("fixed IP address doesn't exist.");
Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();
boolean validFixedIP = false;
while (i.hasNext() && !validFixedIP) {
validFixedIP = true;
}
if (!validFixedIP)
- return Response.status(400).build();
+ throw new BadRequestException("can't find a valid fixed IP address");
} else {
fixedIP = port.getFixedIPs().get(0).getIpAddress();
if (singleton.getFixedIPAddress() != null && !fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))
- return Response.status(400).build();
+ throw new BadRequestException("mismatched fixed IP address in request");
}
//lastly check that this fixed IP address isn't already used
if (port.isBoundToFloatingIP(fixedIP))
- return Response.status(409).build();
+ throw new ResourceConflictException("fixed IP is in use.");
singleton.setFixedIPAddress(fixedIP);
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronFloatingIPAware.class, this, null);
}
}
} else {
- return Response.status(400).build();
+ throw new BadRequestException("only singleton requests allowed.");
}
return Response.status(201).entity(input).build();
}
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
if (!floatingIPInterface.floatingIPExists(floatingipUUID))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
NeutronFloatingIP sourceFloatingIP = floatingIPInterface.getFloatingIP(floatingipUUID);
if (!input.isSingleton())
- return Response.status(400).build();
+ throw new BadRequestException("only singleton requests allowed.");
NeutronFloatingIP singleton = input.getSingleton();
if (singleton.getID() != null)
- return Response.status(400).build();
+ throw new BadRequestException("singleton UUID doesn't exist.");
NeutronNetwork externNetwork = networkInterface.getNetwork(
sourceFloatingIP.getFloatingNetworkUUID());
String floatingIP = singleton.getFloatingIPAddress();
if (floatingIP != null) {
if (externNetwork.getSubnets().size() > 1)
- return Response.status(400).build();
+ throw new BadRequestException("external network doesn't have a subnet.");
NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
if (!externSubnet.isValidIP(floatingIP))
- return Response.status(400).build();
+ throw new BadRequestException("floating IP not valid for external subnet");
if (externSubnet.isIPInUse(floatingIP))
- return Response.status(409).build();
+ throw new ResourceConflictException("floating IP is in use.");
}
// if port_id is specified, then check that the port exists and has at least one IP
if (port_id != null) {
String fixedIP = null; // used for the fixedIP calculation
if (!portInterface.portExists(port_id))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Port UUID doesn't exist.");
NeutronPort port = portInterface.getPort(port_id);
if (port.getFixedIPs().size() < 1)
- return Response.status(400).build();
+ throw new BadRequestException("port ID doesn't have a fixed IP address.");
// if there is more than one fixed IP then check for fixed_ip_address
// and that it is in the list of port addresses
if (port.getFixedIPs().size() > 1) {
fixedIP = singleton.getFixedIPAddress();
if (fixedIP == null)
- return Response.status(400).build();
+ throw new BadRequestException("request doesn't have a fixed IP address");
Iterator<Neutron_IPs> i = port.getFixedIPs().iterator();
boolean validFixedIP = false;
while (i.hasNext() && !validFixedIP) {
validFixedIP = true;
}
if (!validFixedIP)
- return Response.status(400).build();
+ throw new BadRequestException("couldn't find a valid fixed IP address");
} else {
fixedIP = port.getFixedIPs().get(0).getIpAddress();
if (singleton.getFixedIPAddress() != null &&
!fixedIP.equalsIgnoreCase(singleton.getFixedIPAddress()))
- return Response.status(400).build();
+ throw new BadRequestException("mismatch in fixed IP addresses");
}
//lastly check that this fixed IP address isn't already used
if (port.isBoundToFloatingIP(fixedIP))
- return Response.status(409).build();
+ throw new ResourceConflictException("fixed IP is in use.");
singleton.setFixedIPAddress(fixedIP);
}
NeutronFloatingIP target = floatingIPInterface.getFloatingIP(floatingipUUID);
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
if (!floatingIPInterface.floatingIPExists(floatingipUUID))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Floating IP UUID doesn't exist.");
// TODO: need to undo port association if it exists
NeutronFloatingIP singleton = floatingIPInterface.getFloatingIP(floatingipUUID);
Object[] instances = ServiceHelper.getGlobalInstances(INeutronFloatingIPAware.class, this, null);
import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.utils.ServiceHelper;
/**
- * Open DOVE Northbound REST APIs for Network.<br>
- * This class provides REST APIs for managing open DOVE internals related to Networks
+ * Neutron Northbound REST APIs for Network.<br>
+ * This class provides REST APIs for managing neutron Networks
*
* <br>
* <br>
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
if (!networkInterface.networkExists(netUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (fields.size() > 0) {
NeutronNetwork ans = networkInterface.getNetwork(netUUID);
* network ID can't already exist
*/
if (networkInterface.networkExists(singleton.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID already exists");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronNetworkAware.class, this, null);
* already in this bulk request
*/
if (networkInterface.networkExists(test.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID already exists");
}
if (testMap.containsKey(test.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID already exists");
}
if (instances != null) {
for (Object instance: instances) {
* network has to exist and only a single delta is supported
*/
if (!networkInterface.networkExists(netUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (!input.isSingleton()) {
- return Response.status(400).build();
+ throw new BadRequestException("only singleton edits supported");
}
NeutronNetwork delta = input.getSingleton();
*/
if (delta.getID() != null || delta.getTenantID() != null ||
delta.getStatus() != null) {
- return Response.status(400).build();
+ throw new BadRequestException("attribute edit blocked by Neutron");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronNetworkAware.class, this, null);
* network has to exist and not be in use before it can be removed
*/
if (!networkInterface.networkExists(netUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (networkInterface.networkInUse(netUUID)) {
- return Response.status(409).build();
+ throw new ResourceConflictException("Network ID in use");
}
NeutronNetwork singleton = networkInterface.getNetwork(netUUID);
import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
import org.opendaylight.controller.networkconfig.neutron.Neutron_IPs;
import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.utils.ServiceHelper;
/**
- * Open DOVE Northbound REST APIs.<br>
- * This class provides REST APIs for managing the open DOVE
+ * Neutron Northbound REST APIs.<br>
+ * This class provides REST APIs for managing neutron port objects
*
* <br>
* <br>
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
if (!portInterface.portExists(portUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("port UUID does not exist.");
}
if (fields.size() > 0) {
NeutronPort ans = portInterface.getPort(portUUID);
* have a valid MAC and the MAC not be in use
*/
if (singleton.getNetworkUUID() == null) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID musy be specified");
}
if (portInterface.portExists(singleton.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("port UUID already exists");
}
if (!networkInterface.networkExists(singleton.getNetworkUUID())) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (singleton.getMacAddress() == null ||
!singleton.getMacAddress().matches(mac_regex)) {
- return Response.status(400).build();
+ throw new BadRequestException("MAC address not properly formatted");
}
if (portInterface.macInUse(singleton.getMacAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("MAC Address is in use.");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronPortAware.class, this, null);
if (instances != null) {
while (fixedIPIterator.hasNext()) {
Neutron_IPs ip = fixedIPIterator.next();
if (ip.getSubnetUUID() == null) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID not specified");
}
if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID must exists");
}
NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
if (!singleton.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID must match that of subnet");
}
if (ip.getIpAddress() != null) {
if (!subnet.isValidIP(ip.getIpAddress())) {
- return Response.status(400).build();
+ throw new BadRequestException("IP address is not valid");
}
if (subnet.isIPInUse(ip.getIpAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("IP address is in use.");
}
}
}
* can't already contain a new port with the same UUID
*/
if (portInterface.portExists(test.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("port UUID already exists");
}
if (testMap.containsKey(test.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("port UUID already exists");
}
for (NeutronPort check : testMap.values()) {
if (test.getMacAddress().equalsIgnoreCase(check.getMacAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("MAC address already allocated");
}
for (Neutron_IPs test_fixedIP : test.getFixedIPs()) {
for (Neutron_IPs check_fixedIP : check.getFixedIPs()) {
if (test_fixedIP.getIpAddress().equals(check_fixedIP.getIpAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("IP address already allocated");
}
}
}
}
testMap.put(test.getID(), test);
if (!networkInterface.networkExists(test.getNetworkUUID())) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (!test.getMacAddress().matches(mac_regex)) {
- return Response.status(400).build();
+ throw new BadRequestException("MAC address not properly formatted");
}
if (portInterface.macInUse(test.getMacAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("MAC address in use");
}
if (instances != null) {
for (Object instance : instances) {
while (fixedIPIterator.hasNext()) {
Neutron_IPs ip = fixedIPIterator.next();
if (ip.getSubnetUUID() == null) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID must be specified");
}
if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID doesn't exists");
}
NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
if (!test.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID must match that of subnet");
}
if (ip.getIpAddress() != null) {
if (!subnet.isValidIP(ip.getIpAddress())) {
- return Response.status(400).build();
+ throw new BadRequestException("ip address not valid");
}
//TODO: need to add consideration for a fixed IP being assigned the same address as a allocated IP in the
//same bulk create
if (subnet.isIPInUse(ip.getIpAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("IP address in use");
}
}
}
// port has to exist and only a single delta is supported
if (!portInterface.portExists(portUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("port UUID does not exist.");
}
NeutronPort target = portInterface.getPort(portUUID);
if (!input.isSingleton()) {
- return Response.status(400).build();
+ throw new BadRequestException("only singleton edit suported");
}
NeutronPort singleton = input.getSingleton();
NeutronPort original = portInterface.getPort(portUUID);
// deltas restricted by Neutron
if (singleton.getID() != null || singleton.getTenantID() != null ||
singleton.getStatus() != null) {
- return Response.status(400).build();
+ throw new BadRequestException("attribute change blocked by Neutron");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronPortAware.class, this, null);
while (fixedIPIterator.hasNext()) {
Neutron_IPs ip = fixedIPIterator.next();
if (ip.getSubnetUUID() == null) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID must be specified");
}
if (!subnetInterface.subnetExists(ip.getSubnetUUID())) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID doesn't exist.");
}
NeutronSubnet subnet = subnetInterface.getSubnet(ip.getSubnetUUID());
if (!target.getNetworkUUID().equalsIgnoreCase(subnet.getNetworkUUID())) {
- return Response.status(400).build();
+ throw new BadRequestException("network UUID must match that of subnet");
}
if (ip.getIpAddress() != null) {
if (!subnet.isValidIP(ip.getIpAddress())) {
- return Response.status(400).build();
+ throw new BadRequestException("invalid IP address");
}
if (subnet.isIPInUse(ip.getIpAddress())) {
- return Response.status(409).build();
+ throw new ResourceConflictException("IP address in use");
}
}
}
// TODO: Support change of security groups
// update the port and return the modified object
- portInterface.updatePort(portUUID, singleton);
+ portInterface.updatePort(portUUID, singleton);
NeutronPort updatedPort = portInterface.getPort(portUUID);
if (instances != null) {
for (Object instance : instances) {
// port has to exist and not be owned by anyone. then it can be removed from the cache
if (!portInterface.portExists(portUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("port UUID does not exist.");
}
NeutronPort port = portInterface.getPort(portUUID);
if (port.getDeviceID() != null ||
import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface;
import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.utils.ServiceHelper;
/**
- * Open DOVE Northbound REST APIs.<br>
- * This class provides REST APIs for managing the open DOVE
+ * Neutron Northbound REST APIs.<br>
+ * This class provides REST APIs for managing neutron routers
*
* <br>
* <br>
throw new ServiceUnavailableException("Router CRUD Interface "
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
- if (!routerInterface.routerExists(routerUUID))
- return Response.status(404).build();
+ if (!routerInterface.routerExists(routerUUID)) {
+ throw new ResourceNotFoundException("Router UUID not found");
+ }
if (fields.size() > 0) {
NeutronRouter ans = routerInterface.getRouter(routerUUID);
return Response.status(200).entity(
* exists and has been designated as "router:external"
*/
if (routerInterface.routerExists(singleton.getID()))
- return Response.status(400).build();
+ throw new BadRequestException("router UUID already exists");
if (singleton.getExternalGatewayInfo() != null) {
String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
if (!networkInterface.networkExists(externNetworkPtr))
- return Response.status(400).build();
+ throw new BadRequestException("External Network Pointer doesn't exist");
NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
if (!externNetwork.isRouterExternal())
- return Response.status(400).build();
+ throw new BadRequestException("External Network Pointer isn't marked as router:external");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
if (instances != null) {
/*
* only singleton router creates supported
*/
- return Response.status(400).build();
+ throw new BadRequestException("Only singleton router creates supported");
}
return Response.status(201).entity(input).build();
}
* router has to exist and only a single delta can be supplied
*/
if (!routerInterface.routerExists(routerUUID))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Router UUID not found");
if (!input.isSingleton())
- return Response.status(400).build();
+ throw new BadRequestException("Only single router deltas supported");
NeutronRouter singleton = input.getSingleton();
NeutronRouter original = routerInterface.getRouter(routerUUID);
*/
if (singleton.getID() != null || singleton.getTenantID() != null ||
singleton.getStatus() != null)
- return Response.status(400).build();
+ throw new BadRequestException("Request attribute change not allowed");
Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
if (instances != null) {
if (singleton.getExternalGatewayInfo() != null) {
String externNetworkPtr = singleton.getExternalGatewayInfo().getNetworkID();
if (!networkInterface.networkExists(externNetworkPtr))
- return Response.status(400).build();
+ throw new BadRequestException("External Network Pointer does not exist");
NeutronNetwork externNetwork = networkInterface.getNetwork(externNetworkPtr);
if (!externNetwork.isRouterExternal())
- return Response.status(400).build();
+ throw new BadRequestException("External Network Pointer isn't marked as router:external");
}
/*
* verify that the router exists and is not in use before removing it
*/
if (!routerInterface.routerExists(routerUUID))
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Router UUID not found");
if (routerInterface.routerInUse(routerUUID))
- return Response.status(409).build();
+ throw new ResourceConflictException("Router UUID in Use");
NeutronRouter singleton = routerInterface.getRouter(routerUUID);
Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
if (instances != null) {
* or a port id, but not both, this code assumes that the plugin has filled everything in for us and so both must be present
*/
if (!routerInterface.routerExists(routerUUID))
- return Response.status(400).build();
+ throw new BadRequestException("Router UUID doesn't exist");
NeutronRouter target = routerInterface.getRouter(routerUUID);
if (input.getSubnetUUID() == null ||
input.getPortUUID() == null)
- return Response.status(400).build();
+ throw new BadRequestException("Must specify at subnet id, port id or both");
// check that the port is part of the subnet
NeutronSubnet targetSubnet = subnetInterface.getSubnet(input.getSubnetUUID());
if (targetSubnet == null)
- return Response.status(400).build();
+ throw new BadRequestException("Subnet id doesn't exist");
NeutronPort targetPort = portInterface.getPort(input.getPortUUID());
if (targetPort == null)
- return Response.status(400).build();
+ throw new BadRequestException("Port id doesn't exist");
if (!targetSubnet.getPortsInSubnet().contains(targetPort))
- return Response.status(400).build();
+ throw new BadRequestException("Port id not part of subnet id");
if (targetPort.getFixedIPs().size() != 1)
- return Response.status(400).build();
+ throw new BadRequestException("Port id must have a single fixedIP address");
if (targetPort.getDeviceID() != null ||
targetPort.getDeviceOwner() != null)
- return Response.status(409).build();
-
+ throw new ResourceConflictException("Target Port already allocated");
Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
if (instances != null) {
for (Object instance : instances) {
// verify the router exists
if (!routerInterface.routerExists(routerUUID))
- return Response.status(400).build();
+ throw new BadRequestException("Router does not exist");
NeutronRouter target = routerInterface.getRouter(routerUUID);
/*
input.getSubnetUUID() != null) {
NeutronPort port = portInterface.getGatewayPort(input.getSubnetUUID());
if (port == null)
- return Response.status(404).build();
+ throw new ResourceNotFoundException("Port UUID not found");
input.setPortUUID(port.getID());
input.setID(target.getID());
input.setTenantID(target.getTenantID());
if (input.getPortUUID() != null &&
input.getSubnetUUID() != null) {
NeutronPort port = portInterface.getPort(input.getPortUUID());
+ if (port == null) {
+ throw new ResourceNotFoundException("Port UUID not found");
+ }
+ if (port.getFixedIPs() == null) {
+ throw new ResourceNotFoundException("Port UUID jas no fixed IPs");
+ }
NeutronSubnet subnet = subnetInterface.getSubnet(input.getSubnetUUID());
+ if (subnet == null) {
+ throw new ResourceNotFoundException("Subnet UUID not found");
+ }
if (!subnet.isValidIP(port.getFixedIPs().get(0).getIpAddress()))
- return Response.status(409).build();
+ throw new ResourceConflictException("Target Port IP not in Target Subnet");
input.setID(target.getID());
input.setTenantID(target.getTenantID());
Object[] instances = ServiceHelper.getGlobalInstances(INeutronRouterAware.class, this, null);
}
// have to specify either a port ID or a subnet ID
- return Response.status(400).build();
+ throw new BadRequestException("Must specify port id or subnet id or both");
}
}
import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceConflictException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
import org.opendaylight.controller.northbound.commons.exception.InternalServerErrorException;
import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
import org.opendaylight.controller.sal.utils.ServiceHelper;
/**
- * Open DOVE Northbound REST APIs.<br>
- * This class provides REST APIs for managing open DOVE internals related to Subnets
+ * Neutron Northbound REST APIs for Subnets.<br>
+ * This class provides REST APIs for managing neutron Subnets
*
* <br>
* <br>
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
if (!subnetInterface.subnetExists(subnetUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("subnet UUID does not exist.");
}
if (fields.size() > 0) {
NeutronSubnet ans = subnetInterface.getSubnet(subnetUUID);
* *then* add the subnet to the cache
*/
if (subnetInterface.subnetExists(singleton.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID already exists");
}
if (!networkInterface.networkExists(singleton.getNetworkUUID())) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (!singleton.isValidCIDR()) {
- return Response.status(400).build();
+ throw new BadRequestException("invaild CIDR");
}
if (!singleton.initDefaults()) {
throw new InternalServerErrorException("subnet object could not be initialized properly");
}
if (singleton.gatewayIP_Pool_overlap()) {
- return Response.status(409).build();
+ throw new ResourceConflictException("IP pool overlaps with gateway");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);
if (instances != null) {
throw new InternalServerErrorException("subnet object could not be initialized properly");
}
if (subnetInterface.subnetExists(test.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID already exists");
}
if (testMap.containsKey(test.getID())) {
- return Response.status(400).build();
+ throw new BadRequestException("subnet UUID already exists");
}
testMap.put(test.getID(), test);
if (!networkInterface.networkExists(test.getNetworkUUID())) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("network UUID does not exist.");
}
if (!test.isValidCIDR()) {
- return Response.status(400).build();
+ throw new BadRequestException("Invalid CIDR");
}
if (test.gatewayIP_Pool_overlap()) {
- return Response.status(409).build();
+ throw new ResourceConflictException("IP pool overlaps with gateway");
}
if (instances != null) {
for (Object instance : instances) {
* verify the subnet exists and there is only one delta provided
*/
if (!subnetInterface.subnetExists(subnetUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("subnet UUID does not exist.");
}
if (!input.isSingleton()) {
- return Response.status(400).build();
+ throw new BadRequestException("Only singleton edit supported");
}
NeutronSubnet delta = input.getSingleton();
NeutronSubnet original = subnetInterface.getSubnet(subnetUUID);
if (delta.getID() != null || delta.getTenantID() != null ||
delta.getIpVersion() != null || delta.getCidr() != null ||
delta.getAllocationPools() != null) {
- return Response.status(400).build();
+ throw new BadRequestException("Attribute edit blocked by Neutron");
}
Object[] instances = ServiceHelper.getGlobalInstances(INeutronSubnetAware.class, this, null);
* verify the subnet exists and it isn't currently in use
*/
if (!subnetInterface.subnetExists(subnetUUID)) {
- return Response.status(404).build();
+ throw new ResourceNotFoundException("subnet UUID does not exist.");
}
if (subnetInterface.subnetInUse(subnetUUID)) {
return Response.status(409).build();
</scm>
<artifactId>sal</artifactId>
- <version>0.7.1-SNAPSHOT</version>
+ <version>0.8.1-SNAPSHOT</version>
<packaging>bundle</packaging>
<build>
}
}
+ /**
+ * Create the reversed edge
+ * @return The reversed edge.
+ */
+ public Edge reverse() {
+ Edge re;
+ try {
+ re = new Edge(this.headNodeConnector, this.tailNodeConnector);
+ } catch (ConstructionException e) {
+ re = null;
+ }
+ return re;
+ }
/**
* getter of edge
*
package org.opendaylight.controller.sal.core;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
this.edges = edges;
}
+ /**
+ * Create the reversed path
+ * @return The reversed path
+ */
+ public Path reverse() {
+ int j = edges.size(); // size always > 0
+ Edge[] aEdges = new Edge[j];
+ for (Edge e : edges) {
+ j--;
+ aEdges[j] = e.reverse();
+ }
+ Path rp;
+ try {
+ rp = new Path(Arrays.asList(aEdges));
+ } catch (ConstructionException ce) {
+ rp = null;
+ }
+ return rp;
+ }
+
+ /**
+ * Return the list of nodes of this path, the first node is the start node
+ * @return the list of nodes
+ */
+ public List<Node> getNodes() {
+ List<Node> nl = new ArrayList<Node>();
+ nl.add(this.getStartNode());
+ for (Edge e : edges) {
+ nl.add(e.getHeadNodeConnector().getNode());
+ }
+ return nl;
+ }
+
/**
* Copy Construct for a path
*