<artifactId>netconf-client</artifactId>
<version>${netconf.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-client</artifactId>
- <version>${netconf.version}</version>
- <type>test-jar</type>
- </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-client</artifactId>
+ <version>${netconf.version}</version>
+ <type>test-jar</type>
+ </dependency>
<!--Netconf config-->
<dependency>
<artifactId>protocol_plugins.stub</artifactId>
<version>${protocol_plugins.stub.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>routing.dijkstra_implementation</artifactId>
<artifactId>sal-remote</artifactId>
<version>${mdsal.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remoterpc-connector</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-rest-connector</artifactId>
<artifactId>sample-toaster-provider</artifactId>
<version>${mdsal.version}</version>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-consumer</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-provider</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>com.sun.jersey.jersey-servlet</artifactId>
package org.opendaylight.protocol.framework;
import com.google.common.base.Preconditions;
+
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
+import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
+
import java.io.Closeable;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*
* @return ChannelFuture representing the binding process
*/
- protected <CH extends Channel> ChannelFuture createServer(SocketAddress address, Class<? extends ServerChannel> channelClass,
- final ChannelPipelineInitializer<CH, S> initializer) {
+ protected <CH extends Channel> ChannelFuture createServer(final SocketAddress address, final Class<? extends ServerChannel> channelClass,
+ final ChannelPipelineInitializer<CH, S> initializer) {
final ServerBootstrap b = new ServerBootstrap();
b.childHandler(new ChannelInitializer<CH>() {
// makes no sense for LocalServer and produces warning
b.childOption(ChannelOption.SO_KEEPALIVE, true);
}
+ b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
customizeBootstrap(b);
if (b.group() == null) {
protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final PipelineInitializer<S> initializer) {
final Bootstrap b = new Bootstrap();
final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<S>(executor, address, strategy, b);
- b.group(this.workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler(
+ b.option(ChannelOption.SO_KEEPALIVE, true).handler(
new ChannelInitializer<SocketChannel>() {
-
@Override
protected void initChannel(final SocketChannel ch) {
initializer.initializeChannel(ch, p);
customizeBootstrap(b);
+ if (b.group() == null) {
+ b.group(workerGroup);
+ }
+
+ // There is no way to detect if this was already set by
+ // customizeBootstrap()
+ try {
+ b.channel(NioSocketChannel.class);
+ } catch (IllegalStateException e) {
+ LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
+ }
+
p.connect();
LOG.debug("Client created.");
return p;
* 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.jmx;
+package org.opendaylight.controller.config.api.jmx;
import javax.management.ObjectName;
package org.opendaylight.controller.config.manager.impl.jmx;
import javax.management.ObjectName;
+import org.opendaylight.controller.config.api.jmx.ServiceReferenceMXBean;
public class ServiceReferenceMXBeanImpl implements ServiceReferenceMXBean {
private ObjectName currentImplementation;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest.RecordingBundleContextServiceRegistrationHandler.RegistrationHolder;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
-import org.opendaylight.controller.config.manager.impl.jmx.ServiceReferenceMXBean;
+import org.opendaylight.controller.config.api.jmx.ServiceReferenceMXBean;
import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
import org.opendaylight.controller.config.manager.testingservices.parallelapsp.test.AbstractParallelAPSPTest;
import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.TestingScheduledThreadPoolModuleFactory;
public interface ConfigSnapshotHolder {
/**
- * Get part of get-config document that contains just
+ * Get XML node that should be pushed to netconf's edit-config
*/
String getConfigSnapshot();
/**
* Get only required capabilities referenced by the snapshot.
+ * If no value is specified, return empty set instead of null
*/
SortedSet<String> getCapabilities();
*/
package org.opendaylight.controller.config.persist.storage.file.xml.model;
+import java.util.TreeSet;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import javax.xml.bind.annotation.XmlAnyElement;
public static final String SNAPSHOT_ROOT_ELEMENT_NAME = "snapshot";
private String configSnapshot;
- private SortedSet<String> capabilities;
+ private SortedSet<String> capabilities = new TreeSet<>();
ConfigSnapshot(String configXml, SortedSet<String> capabilities) {
this.configSnapshot = configXml;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.opendaylight.controller.config.api.jmx.ServiceReferenceMXBean;
public class ConfigRegistryJMXClient implements ConfigRegistryClient {
private final ConfigRegistryMXBean configRegistryMXBeanProxy;
configMBeanServer);
}
+ /**
+ * Usage of this method indicates error as config JMX uses solely MXBeans.
+ * Use {@link #newMXBeanProxy(javax.management.ObjectName, Class)}
+ * or {@link JMX#newMBeanProxy(javax.management.MBeanServerConnection, javax.management.ObjectName, Class)}
+ * This method will be removed soon.
+ */
+ @Deprecated
public <T> T newMBeanProxy(ObjectName on, Class<T> clazz) {
+ on = translateServiceRefIfPossible(on, clazz, configMBeanServer);
return JMX.newMBeanProxy(configMBeanServer, on, clazz);
}
+ static ObjectName translateServiceRefIfPossible(ObjectName on, Class<?> clazz, MBeanServer configMBeanServer) {
+ if (ObjectNameUtil.isServiceReference(on) && clazz.equals(ServiceReferenceMXBean.class) == false) {
+ ServiceReferenceMXBean proxy = JMX.newMXBeanProxy(configMBeanServer, on, ServiceReferenceMXBean.class);
+ on = proxy.getCurrentImplementation();
+ }
+ return on;
+ }
+
+
public <T> T newMXBeanProxy(ObjectName on, Class<T> clazz) {
return JMX.newMXBeanProxy(configMBeanServer, on, clazz);
}
public void checkServiceReferenceExists(ObjectName objectName) throws InstanceNotFoundException {
configRegistryMXBeanProxy.checkServiceReferenceExists(objectName);
}
-
}
*/
package org.opendaylight.controller.config.util;
-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;
import javax.management.Attribute;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
-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;
public class ConfigTransactionJMXClient implements ConfigTransactionClient {
private final ConfigRegistryMXBean configRegistryMXBeanProxy;
}
public <T> T newMXBeanProxy(ObjectName on, Class<T> clazz) {
+ // if on is without transaction, add it. Reason is that when using getters on MXBeans the transaction name is stripped
+ on = ObjectNameUtil.withTransactionName(on, getTransactionName());
+ // if this is service reference and user requests for implementation, look it up
+ on = ConfigRegistryJMXClient.translateServiceRefIfPossible(on, clazz, configMBeanServer);
+ on = ObjectNameUtil.withTransactionName(on, getTransactionName());
return JMX.newMXBeanProxy(configMBeanServer, on, clazz);
}
+ /**
+ * Usage of this method indicates error as config JMX uses solely MXBeans.
+ * Use {@link #newMXBeanProxy(javax.management.ObjectName, Class)}
+ * or {@link JMX#newMBeanProxy(javax.management.MBeanServerConnection, javax.management.ObjectName, Class)}
+ * This method will be removed soon.
+ */
+ @Deprecated
public <T> T newMBeanProxy(ObjectName on, Class<T> clazz) {
return JMX.newMBeanProxy(configMBeanServer, on, clazz);
}
*/
package org.opendaylight.controller.config.yangjmxgenerator.plugin.gofactory;
+import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import com.google.common.base.Joiner;
"}\n", moduleField.getName(), osgi);
} else {
str = format(
- "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);",
+ "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);\n",
moduleField.getName(), osgi);
}
resolveDependenciesMap.put(moduleField, str);
// wrap each field resolvation statement with if !=null when dependency is not mandatory
for (Map.Entry<ModuleField, String> entry : resolveDependenciesMap.entrySet()) {
if (entry.getKey().getDependency().isMandatory() == false) {
- result += format("if (%s!=null) {\n%s;\n}", entry.getKey().getName(), entry.getValue());
+ checkState(entry.getValue().endsWith(";\n"));
+ result += format("if (%s!=null) {\n%s}\n", entry.getKey().getName(), entry.getValue());
} else {
result += entry.getValue();
}
format("private final %s oldModule;\n", abstractFQN.getTypeName())+
format("private final %s oldInstance;\n", AutoCloseable.class.getCanonicalName())+
format("private %s instance;\n", AutoCloseable.class.getCanonicalName())+
- format("private final %s dependencyResolver;\n", DependencyResolver.class.getCanonicalName())+
+ format("protected final %s dependencyResolver;\n", DependencyResolver.class.getCanonicalName())+
format("private final %s identifier;\n", ModuleIdentifier.class.getCanonicalName())+
"@Override\n"+
format("public %s getIdentifier() {\n", ModuleIdentifier.class.getCanonicalName())+
parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
- String setToNulls = "this.oldInstance=null;\n;" +
+ String setToNulls = "this.oldInstance=null;\n" +
"this.oldModule=null;\n";
return getConstructorStart(abstractFQN, parameters, setToNulls);
}
*/
package org.opendaylight.controller.config.yangjmxgenerator;
-import java.net.URI;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
import org.opendaylight.yangtools.yang.common.QName;
public class ConfigConstants {
public static final QName RPC_CONTEXT_REF_GROUPING_LEAF = createRpcXQName("context-instance");
public static final QName RPC_CONTEXT_INSTANCE_EXTENSION_QNAME = createRpcXQName("rpc-context-instance");
- public static QName createConfigQName(String localName) {
- return createQName(CONFIG_NAMESPACE, "2013-04-05", localName);
+ public static QName createConfigQName(final String localName) {
+ // FIXME: pre-construct QNameModule
+ return QName.create(CONFIG_NAMESPACE, "2013-04-05", localName);
}
- public static QName createRpcXQName(String localName) {
- return createQName("urn:ietf:params:xml:ns:yang:rpc-context",
+ public static QName createRpcXQName(final String localName) {
+ // FIXME: pre-construct QNameModule
+ return QName.create("urn:ietf:params:xml:ns:yang:rpc-context",
"2013-06-17", localName);
}
-
- /**
- *
- * @param uri
- * @param revisionDate
- * in format yyyy-MM-dd
- * @param localName
- * @return
- */
- private static QName createQName(String uri, String revisionDate,
- String localName) {
- SimpleDateFormat revisionFormat = new SimpleDateFormat("yyyy-MM-dd");
- Date revision;
- try {
- revision = revisionFormat.parse(revisionDate);
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- return new QName(URI.create(uri), revision, localName);
- }
-
}
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import javax.annotation.Nullable;
+
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
private TypeProviderWrapper typeProviderWrapper;
private String packageName;
- public ModuleMXBeanEntryBuilder setModule(Module module) {
+ public ModuleMXBeanEntryBuilder setModule(final Module module) {
this.currentModule = module;
return this;
}
- public ModuleMXBeanEntryBuilder setqNamesToSIEs(Map<QName, ServiceInterfaceEntry> qNamesToSIEs) {
+ public ModuleMXBeanEntryBuilder setqNamesToSIEs(final Map<QName, ServiceInterfaceEntry> qNamesToSIEs) {
this.qNamesToSIEs = qNamesToSIEs;
return this;
}
- public ModuleMXBeanEntryBuilder setSchemaContext(SchemaContext schemaContext) {
+ public ModuleMXBeanEntryBuilder setSchemaContext(final SchemaContext schemaContext) {
this.schemaContext = schemaContext;
return this;
}
- public ModuleMXBeanEntryBuilder setTypeProviderWrapper(TypeProviderWrapper typeProviderWrapper) {
+ public ModuleMXBeanEntryBuilder setTypeProviderWrapper(final TypeProviderWrapper typeProviderWrapper) {
this.typeProviderWrapper = typeProviderWrapper;
return this;
}
- public ModuleMXBeanEntryBuilder setPackageName(String packageName) {
+ public ModuleMXBeanEntryBuilder setPackageName(final String packageName) {
this.packageName = packageName;
return this;
}
return result;
}
- private static void cleanUpNulls(Map<String, ModuleMXBeanEntry> result) {
+ private static void cleanUpNulls(final Map<String, ModuleMXBeanEntry> result) {
for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
ModuleMXBeanEntry module = entry.getValue();
if (module.getAttributes() == null) {
}
}
- private static void checkUnaugumentedIdentities(Map<String, IdentitySchemaNode> unaugmentedModuleIdentities) {
+ private static void checkUnaugumentedIdentities(final Map<String, IdentitySchemaNode> unaugmentedModuleIdentities) {
if (unaugmentedModuleIdentities.size() > 0) {
logger.warn("Augmentation not found for all currentModule identities: {}",
unaugmentedModuleIdentities.keySet());
}
}
- private static void checkAttributeNamesUniqueness(Map<String, QName> uniqueGeneratedClassesNames, Map<String, ModuleMXBeanEntry> result) {
+ private static void checkAttributeNamesUniqueness(final Map<String, QName> uniqueGeneratedClassesNames, final Map<String, ModuleMXBeanEntry> result) {
for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
checkUniqueRuntimeBeanAttributesName(entry.getValue(),
uniqueGeneratedClassesNames);
return moduleIdentities;
}
- private Collection<ChoiceCaseNode> castChildNodesToChoiceCases(Set<DataSchemaNode> childNodes) {
+ private Collection<ChoiceCaseNode> castChildNodesToChoiceCases(final Set<DataSchemaNode> childNodes) {
return Collections2.transform(childNodes, new Function<DataSchemaNode, ChoiceCaseNode>() {
@Nullable
@Override
- public ChoiceCaseNode apply(@Nullable DataSchemaNode input) {
+ public ChoiceCaseNode apply(@Nullable final DataSchemaNode input) {
return (ChoiceCaseNode) input;
}
});
}
- private boolean areAllChildrenChoiceCaseNodes(Set<DataSchemaNode> childNodes) {
+ private boolean areAllChildrenChoiceCaseNodes(final Set<DataSchemaNode> childNodes) {
for (DataSchemaNode childNode : childNodes) {
- if (childNode instanceof ChoiceCaseNode == false)
+ if (childNode instanceof ChoiceCaseNode == false) {
return false;
+ }
}
return true;
}
- private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> void processChoiceCaseNode(Map<String, ModuleMXBeanEntry> result,
- Map<String, QName> uniqueGeneratedClassesNames, String configModulePrefix,
- Map<String, IdentitySchemaNode> moduleIdentities,
- Map<String, IdentitySchemaNode> unaugmentedModuleIdentities, AugmentationSchema augmentation,
- DataSchemaNode when) {
+ private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> void processChoiceCaseNode(final Map<String, ModuleMXBeanEntry> result,
+ final Map<String, QName> uniqueGeneratedClassesNames, final String configModulePrefix,
+ final Map<String, IdentitySchemaNode> moduleIdentities,
+ final Map<String, IdentitySchemaNode> unaugmentedModuleIdentities, final AugmentationSchema augmentation,
+ final DataSchemaNode when) {
ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when;
if (choiceCaseNode.getConstraints() == null || choiceCaseNode.getConstraints().getWhenCondition() == null) {
}
checkState(Objects.equals(nullableDummyContainerName, moduleMXBeanEntry.getNullableDummyContainerName()),
"Mismatch in module " + moduleMXBeanEntry.toString() + " - dummy container must be present/missing in" +
- " both state and configuration");
+ " both state and configuration");
} else {
ModuleMXBeanEntry.ModuleMXBeanEntryInitial initial = new ModuleMXBeanEntry.ModuleMXBeanEntryInitialBuilder()
- .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix)
- .setNamespace(currentModule.getNamespace().toString()).setqName(ModuleUtil.getQName(currentModule))
- .build();
+ .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix)
+ .setNamespace(currentModule.getNamespace().toString()).setqName(ModuleUtil.getQName(currentModule))
+ .build();
// construct ModuleMXBeanEntry
ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(initial, yangToAttributes, providedServices,
}
}
- private void checkUniqueRuntimeBeansGeneratedClasses(Map<String, QName> uniqueGeneratedClassesNames,
- DataSchemaNode when, Collection<RuntimeBeanEntry> runtimeBeans) {
+ private void checkUniqueRuntimeBeansGeneratedClasses(final Map<String, QName> uniqueGeneratedClassesNames,
+ final DataSchemaNode when, final Collection<RuntimeBeanEntry> runtimeBeans) {
for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
final String javaNameOfRuntimeMXBean = runtimeBean.getJavaNameOfRuntimeMXBean();
if (uniqueGeneratedClassesNames.containsKey(javaNameOfRuntimeMXBean)) {
}
}
- private static void checkUniqueRuntimeBeanAttributesName(ModuleMXBeanEntry mxBeanEntry,
- Map<String, QName> uniqueGeneratedClassesNames) {
+ private static void checkUniqueRuntimeBeanAttributesName(final ModuleMXBeanEntry mxBeanEntry,
+ final Map<String, QName> uniqueGeneratedClassesNames) {
for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) {
for (String runtimeAttName : runtimeBeanEntry.getYangPropertiesToTypesMap().keySet()) {
if (mxBeanEntry.getAttributes().keySet().contains(runtimeAttName)) {
}
}
- private void checkUniqueAttributesWithGeneratedClass(Map<String, QName> uniqueGeneratedClassNames,
- QName parentQName, Map<String, AttributeIfc> yangToAttributes) {
+ private void checkUniqueAttributesWithGeneratedClass(final Map<String, QName> uniqueGeneratedClassNames,
+ final QName parentQName, final Map<String, AttributeIfc> yangToAttributes) {
for (Map.Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
if (attr.getValue() instanceof TOAttribute) {
checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, (TOAttribute) attr.getValue());
}
}
- private void checkUniqueTOAttr(Map<String, QName> uniqueGeneratedClassNames, QName parentQName, TOAttribute attr) {
+ private void checkUniqueTOAttr(final Map<String, QName> uniqueGeneratedClassNames, final QName parentQName, final TOAttribute attr) {
final String upperCaseCamelCase = attr.getUpperCaseCammelCase();
if (uniqueGeneratedClassNames.containsKey(upperCaseCamelCase)) {
QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCamelCase);
}
}
- private Collection<RuntimeBeanEntry> fillRuntimeBeans(DataNodeContainer dataNodeContainer, Module currentModule,
- TypeProviderWrapper typeProviderWrapper, String packageName, String moduleLocalNameFromXPath,
- String javaNamePrefix) {
+ private Collection<RuntimeBeanEntry> fillRuntimeBeans(final DataNodeContainer dataNodeContainer, final Module currentModule,
+ final TypeProviderWrapper typeProviderWrapper, final String packageName, final String moduleLocalNameFromXPath,
+ final String javaNamePrefix) {
return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, dataNodeContainer, moduleLocalNameFromXPath,
typeProviderWrapper, javaNamePrefix, currentModule).values();
* @param choiceCaseNode state or configuration case statement
* @return either choiceCaseNode or its only child container
*/
- private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> HAS_CHILDREN_AND_QNAME getDataNodeContainer(ChoiceCaseNode choiceCaseNode) {
+ private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> HAS_CHILDREN_AND_QNAME getDataNodeContainer(final ChoiceCaseNode choiceCaseNode) {
Set<DataSchemaNode> childNodes = choiceCaseNode.getChildNodes();
if (childNodes.size() == 1) {
DataSchemaNode onlyChild = childNodes.iterator().next();
return (HAS_CHILDREN_AND_QNAME) choiceCaseNode;
}
- private Map<String, AttributeIfc> fillConfiguration(DataNodeContainer dataNodeContainer, Module currentModule,
- TypeProviderWrapper typeProviderWrapper, Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
- SchemaContext schemaContext, String packageName) {
+ private Map<String, AttributeIfc> fillConfiguration(final DataNodeContainer dataNodeContainer, final Module currentModule,
+ final TypeProviderWrapper typeProviderWrapper, final Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+ final SchemaContext schemaContext, final String packageName) {
Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
Set<DataSchemaNode> childNodes = dataNodeContainer.getChildNodes();
for (DataSchemaNode attrNode : childNodes) {
return yangToAttributes;
}
- private Map<String, QName> findProvidedServices(IdentitySchemaNode moduleIdentity, Module currentModule,
- Map<QName, ServiceInterfaceEntry> qNamesToSIEs, SchemaContext schemaContext) {
+ private Map<String, QName> findProvidedServices(final IdentitySchemaNode moduleIdentity, final Module currentModule,
+ final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final SchemaContext schemaContext) {
Map<String, QName> result = new HashMap<>();
for (UnknownSchemaNode unknownNode : moduleIdentity.getUnknownSchemaNodes()) {
if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
return result;
}
- private AttributeIfc getAttributeValue(DataSchemaNode attrNode, Module currentModule,
- Map<QName, ServiceInterfaceEntry> qNamesToSIEs, TypeProviderWrapper typeProviderWrapper,
- SchemaContext schemaContext, String packageName) {
+ private AttributeIfc getAttributeValue(final DataSchemaNode attrNode, final Module currentModule,
+ final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final TypeProviderWrapper typeProviderWrapper,
+ final SchemaContext schemaContext, final String packageName) {
if (attrNode instanceof LeafSchemaNode) {
// simple type
}
}
- private Optional<? extends AbstractDependencyAttribute> extractDependency(DataNodeContainer dataNodeContainer,
- DataSchemaNode attrNode, Module currentModule, Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
- SchemaContext schemaContext) {
+ private Optional<? extends AbstractDependencyAttribute> extractDependency(final DataNodeContainer dataNodeContainer,
+ final DataSchemaNode attrNode, final Module currentModule, final Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+ final SchemaContext schemaContext) {
if (dataNodeContainer.getUses().size() == 1 && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) {
// reference
UsesNode usesNode = dataNodeContainer.getUses().iterator().next();
return Optional.absent();
}
- private int getChildNodeSizeWithoutUses(DataNodeContainer csn) {
+ private int getChildNodeSizeWithoutUses(final DataNodeContainer csn) {
int result = 0;
for (DataSchemaNode dsn : csn.getChildNodes()) {
if (dsn.isAddedByUses() == false) {
return result;
}
- private ServiceInterfaceEntry findSIE(String prefixAndIdentityLocalName, Module currentModule,
- Map<QName, ServiceInterfaceEntry> qNamesToSIEs, SchemaContext schemaContext) {
+ private ServiceInterfaceEntry findSIE(final String prefixAndIdentityLocalName, final Module currentModule,
+ final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final SchemaContext schemaContext) {
Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
Module foundModule;
foundModule = currentModule; // no prefix => SIE is in currentModule
localSIName = prefixAndIdentityLocalName;
}
- QName siQName = new QName(foundModule.getNamespace(), foundModule.getRevision(), localSIName);
+ QName siQName = QName.create(foundModule.getNamespace(), foundModule.getRevision(), localSIName);
ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
checkState(sie != null, "Cannot find referenced Service Interface by " + prefixAndIdentityLocalName);
return sie;
}
- private ModuleImport findModuleImport(Module module, String prefix) {
+ private ModuleImport findModuleImport(final Module module, final String prefix) {
for (ModuleImport moduleImport : module.getImports()) {
if (moduleImport.getPrefix().equals(prefix)) {
return moduleImport;
}
@VisibleForTesting
- static Matcher getWhenConditionMatcher(String prefix, RevisionAwareXPath whenConstraint) {
+ static Matcher getWhenConditionMatcher(final String prefix, final RevisionAwareXPath whenConstraint) {
String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(MAGIC_STRING, prefix);
Pattern pattern = Pattern.compile(xpathRegex);
return pattern.matcher(whenConstraint.toString());
}
- String getConfigModulePrefixFromImport(Module currentModule) {
+ String getConfigModulePrefixFromImport(final Module currentModule) {
for (ModuleImport currentImport : currentModule.getImports()) {
if (currentImport.getModuleName().equals(ConfigConstants.CONFIG_MODULE)) {
return currentImport.getPrefix();
public class ModuleUtil {
- public static QName getQName(Module currentModule){
- return new QName(currentModule.getNamespace(), currentModule.getRevision(), currentModule.getName());
+ public static QName getQName(final Module currentModule) {
+ return QName.create(currentModule.getNamespace(), currentModule.getRevision(), currentModule.getName());
}
}
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
private final Set<Rpc> rpcs;
@VisibleForTesting
- RuntimeBeanEntry(String packageName,
- DataNodeContainer nodeForReporting, String yangName,
- String javaNamePrefix, boolean isRoot,
- Optional<String> keyYangName, List<AttributeIfc> attributes,
- List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
+ RuntimeBeanEntry(final String packageName,
+ final DataNodeContainer nodeForReporting, final String yangName,
+ final String javaNamePrefix, final boolean isRoot,
+ final Optional<String> keyYangName, final List<AttributeIfc> attributes,
+ final List<RuntimeBeanEntry> children, final Set<Rpc> rpcs) {
checkArgument(isRoot == false || keyYangName.isPresent() == false,
"Root RuntimeBeanEntry must not have key " + "set");
for (AttributeIfc a : attributes) {
checkState(map.containsKey(a.getAttributeYangName()) == false,
"Attribute already defined: " + a.getAttributeYangName()
- + " in " + nodeForReporting);
+ + " in " + nodeForReporting);
map.put(a.getAttributeYangName(), a);
}
* not contain special configuration for it.
*/
public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
- String packageName, DataNodeContainer container,
- String moduleYangName, TypeProviderWrapper typeProviderWrapper,
- String javaNamePrefix, Module currentModule) {
+ final String packageName, final DataNodeContainer container,
+ final String moduleYangName, final TypeProviderWrapper typeProviderWrapper,
+ final String javaNamePrefix, final Module currentModule) {
Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
}
private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
- Module currentModule) {
+ final Module currentModule) {
// currently only looks for local identities (found in currentModule)
Map<QName, Set<RpcDefinition>> result = new HashMap<>();
for (IdentitySchemaNode identity : currentModule.getIdentities()) {
if (input != null) {
for (UsesNode uses : input.getUses()) {
- if (uses.getGroupingPath().getPath().size() != 1)
+ if (uses.getGroupingPath().getPath().size() != 1) {
continue;
+ }
// check grouping path
QName qname = uses.getGroupingPath().getPath().get(0);
if (false == qname
- .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
+ .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME)) {
continue;
+ }
for (SchemaNode refinedNode : uses.getRefines().values()) {
.equals(unknownSchemaNode.getNodeType())) {
String localIdentityName = unknownSchemaNode
.getNodeParameter();
- QName identityQName = new QName(
+ QName identityQName = QName.create(
currentModule.getNamespace(),
currentModule.getRevision(),
localIdentityName);
* in subtree.
*/
private static AttributesRpcsAndRuntimeBeans extractSubtree(
- String packageName, DataNodeContainer subtree,
- TypeProviderWrapper typeProviderWrapper, Module currentModule,
- Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
+ final String packageName, final DataNodeContainer subtree,
+ final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
+ final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
List<AttributeIfc> attributes = Lists.newArrayList();
// List<JavaAttribute> javaAttributes = new ArrayList<>();
if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
.equals(unknownSchemaNode.getNodeType())) {
String localIdentityName = unknownSchemaNode.getNodeParameter();
- QName identityQName = new QName(currentModule.getNamespace(),
+ QName identityQName = QName.create(currentModule.getNamespace(),
currentModule.getRevision(), localIdentityName);
Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
.get(identityQName);
for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
.getChildNodes())) {
if (childNode.isAddedByUses() == false) { // skip
- // refined
- // context-instance
+ // refined
+ // context-instance
checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
+ "Currently only leafs and empty output nodes are supported, got " + childNode);
JavaAttribute javaAttribute = new JavaAttribute(
attributes, rpcs);
}
- private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper,
- String packageName) {
+ private static AttributeIfc getReturnTypeAttribute(final DataSchemaNode child, final TypeProviderWrapper typeProviderWrapper,
+ final String packageName) {
if (child instanceof LeafSchemaNode) {
LeafSchemaNode leaf = (LeafSchemaNode) child;
return new JavaAttribute(leaf, typeProviderWrapper);
}
}
- private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
+ private static Collection<DataSchemaNode> sortAttributes(final Set<DataSchemaNode> childNodes) {
final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
@Override
- public int compare(DataSchemaNode o1, DataSchemaNode o2) {
+ public int compare(final DataSchemaNode o1, final DataSchemaNode o2) {
return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
}
});
return dataSchemaNodes;
}
- private static boolean isInnerStateBean(DataSchemaNode child) {
+ private static boolean isInnerStateBean(final DataSchemaNode child) {
for (UnknownSchemaNode unknownSchemaNode : child
.getUnknownSchemaNodes()) {
if (unknownSchemaNode.getNodeType().equals(
- ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
+ ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME)) {
return true;
+ }
}
return false;
}
- private static RuntimeBeanEntry createHierarchical(String packageName,
- ListSchemaNode listSchemaNode,
- TypeProviderWrapper typeProviderWrapper, Module currentModule,
- Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
+ private static RuntimeBeanEntry createHierarchical(final String packageName,
+ final ListSchemaNode listSchemaNode,
+ final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
+ final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
// supported are numeric types, strings, enums
// get all attributes
return rbFromAttributes;
}
- private static RuntimeBeanEntry createRoot(String packageName,
- DataNodeContainer nodeForReporting, String attributeYangName,
- List<AttributeIfc> attributes, String javaNamePrefix,
- List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
+ private static RuntimeBeanEntry createRoot(final String packageName,
+ final DataNodeContainer nodeForReporting, final String attributeYangName,
+ final List<AttributeIfc> attributes, final String javaNamePrefix,
+ final List<RuntimeBeanEntry> children, final Set<Rpc> rpcs) {
return new RuntimeBeanEntry(packageName, nodeForReporting,
attributeYangName, javaNamePrefix, true,
Optional.<String> absent(), attributes, children, rpcs);
private final Set<Rpc> rpcs;
public AttributesRpcsAndRuntimeBeans(
- List<RuntimeBeanEntry> runtimeBeanEntries,
- List<AttributeIfc> attributes, Set<Rpc> rpcs) {
+ final List<RuntimeBeanEntry> runtimeBeanEntries,
+ final List<AttributeIfc> attributes, final Set<Rpc> rpcs) {
this.runtimeBeanEntries = runtimeBeanEntries;
this.attributes = attributes;
this.rpcs = rpcs;
private final AttributeIfc returnType;
private final String yangName;
- Rpc(AttributeIfc returnType, String name, String yangName,
- List<JavaAttribute> parameters) {
+ Rpc(final AttributeIfc returnType, final String name, final String yangName,
+ final List<JavaAttribute> parameters) {
this.returnType = returnType;
this.name = name;
this.parameters = parameters;
return getJavaNameOfRuntimeMXBean(javaNamePrefix);
}
- public String getFullyQualifiedName(String typeName) {
+ public String getFullyQualifiedName(final String typeName) {
return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
typeName);
}
- private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
+ private static String getJavaNameOfRuntimeMXBean(final String javaNamePrefix) {
return javaNamePrefix + MXBEAN_SUFFIX;
}
*/
package org.opendaylight.controller.config.yangjmxgenerator;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
import com.google.common.collect.Sets;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.SimpleType;
+
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
-import javax.management.openmbean.ArrayType;
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.SimpleType;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
public class ModuleMXBeanEntryTest extends AbstractYangTest {
public static final String PACKAGE_NAME = "pack2";
PACKAGE_NAME,identitiesToSIs));
Map<String /* identity local name */, ModuleMXBeanEntry> namesToMBEs = ModuleMXBeanEntry
.create(jmxImplModule, modulesToSIEs, context, new TypeProviderWrapper(new TypeProviderImpl(context))
- , PACKAGE_NAME);
+ , PACKAGE_NAME);
Map<String, AttributeIfc> attributes = namesToMBEs.get("impl-netconf")
.getAttributes();
assertThat(threadFactoryAttribute.getType().getName(), is("ObjectName"));
}
- private void assertCorrectAttributesSize(Map<String, ModuleMXBeanEntry> namesToMBEs, Map<String, AttributeIfc> attributes) {
+ private void assertCorrectAttributesSize(final Map<String, ModuleMXBeanEntry> namesToMBEs, final Map<String, AttributeIfc> attributes) {
assertEquals(14, attributes.size());
assertEquals(1, namesToMBEs.get("impl-netconf").getRuntimeBeans().size());
assertEquals(2, namesToMBEs.get("impl-netconf").getRuntimeBeans().iterator().next().getAttributes().size());
}
protected RuntimeBeanEntry findFirstByYangName(
- Collection<RuntimeBeanEntry> runtimeBeans, String yangName) {
+ final Collection<RuntimeBeanEntry> runtimeBeans, final String yangName) {
for (RuntimeBeanEntry rb : runtimeBeans) {
- if (yangName.equals(rb.getYangName()))
+ if (yangName.equals(rb.getYangName())) {
return rb;
+ }
}
throw new IllegalArgumentException("Yang name not found:" + yangName
+ " in " + runtimeBeans);
"/config:modules/config:module/config:type=\"threadpool-dynamic\"");
}
- private void assertMatches(String prefix, String input) {
+ private void assertMatches(final String prefix, final String input) {
RevisionAwareXPath whenConstraint = mock(RevisionAwareXPath.class);
doReturn(input).when(whenConstraint).toString();
Matcher output = ModuleMXBeanEntryBuilder.getWhenConditionMatcher(prefix,
is((Type) Types.typeForClass(Long.class)));
}
// check dependency on thread factory
- QName threadfactoryQName = new QName(THREADS_NAMESPACE,
+ QName threadfactoryQName = QName.create(THREADS_NAMESPACE,
THREADS_REVISION_DATE, "threadfactory");
ServiceInterfaceEntry threadFactorySIEntry = modulesToSIEs
.get(threadfactoryQName);
DataSchemaNode mockedDataSchemaNode = mock(DataSchemaNode.class);
doReturn(Collections.emptyList()).when(mockedDataSchemaNode)
- .getUnknownSchemaNodes();
+ .getUnknownSchemaNodes();
doReturn(threadfactoryQName).when(mockedDataSchemaNode).getQName();
AttributeIfc expectedDependencyAttribute = new DependencyAttribute(
mockedDataSchemaNode, threadFactorySIEntry,
assertThat(actualThreadFactory, is(expectedDependencyAttribute));
assertThat(
dynamicThreadPool
- .getFullyQualifiedName("DynamicThreadPoolModuleMXBean"),
+ .getFullyQualifiedName("DynamicThreadPoolModuleMXBean"),
is(PACKAGE_NAME + ".DynamicThreadPoolModuleMXBean"));
assertThat(dynamicThreadPool.getNullableDescription(),
is("threadpool-dynamic description"));
*/
package org.opendaylight.controller.config.yangjmxgenerator;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
import com.google.common.collect.Sets;
+
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
public class ServiceInterfaceEntryTest extends AbstractYangTest {
public static final String PACKAGE_NAME = "packages.sis";
}
}
- public static final QName EVENTBUS_QNAME = new QName(THREADS_NAMESPACE,
+ public static final QName EVENTBUS_QNAME = QName.create(THREADS_NAMESPACE,
THREADS_REVISION_DATE, "eventbus");
- public static final QName THREADFACTORY_QNAME = new QName(
+ public static final QName THREADFACTORY_QNAME = QName.create(
THREADS_NAMESPACE, THREADS_REVISION_DATE, "threadfactory");
- public static final QName THREADPOOL_QNAME = new QName(THREADS_NAMESPACE,
+ public static final QName THREADPOOL_QNAME = QName.create(THREADS_NAMESPACE,
THREADS_REVISION_DATE, "threadpool");
- public static final QName SCHEDULED_THREADPOOL_QNAME = new QName(
+ public static final QName SCHEDULED_THREADPOOL_QNAME = QName.create(
THREADS_NAMESPACE, THREADS_REVISION_DATE, "scheduled-threadpool");
- public static final QName SCHEDULED_EXECUTOR_SERVICE_QNAME = new QName(
+ public static final QName SCHEDULED_EXECUTOR_SERVICE_QNAME = QName.create(
THREADS_NAMESPACE, THREADS_REVISION_DATE,
"scheduled-executor-service");
public static final String SCHEDULED_THREADPOOL_INTERFACE_NAME = "ScheduledThreadPoolServiceInterface";
+ SCHEDULED_THREADPOOL_INTERFACE_NAME));
}
- static String trimInnerSpacesOrNull(String input) {
- if (input == null)
+ static String trimInnerSpacesOrNull(final String input) {
+ if (input == null) {
return null;
+ }
return input.replaceAll("\\s{2,}", " ");
}
}
<artifactId>protocol-framework</artifactId>
</dependency>
- <!-- clustering -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>${mdsal.version}</version>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-remote</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remoterpc-connector</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-rest-connector</artifactId>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
<name>binding-data-broker</name>
</data-broker>
+ <root-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-async-data-broker</type>
+ <name>binding-data-broker</name>
+ </root-data-broker>
</module>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ 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
+-->
+<snapshot>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <!-- Netconf dispatcher to be used by all netconf-connectors -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">prefix:netconf-client-dispatcher</type>
+ <name>global-netconf-dispatcher</name>
+ <boss-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-boss-group</name>
+ </boss-thread-group>
+ <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <timer xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <name>global-timer</name>
+ </timer>
+ </module>
+
+ <!-- Thread factory to be used by all threadpools in netconf-connectors -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
+ </module>
+ <!-- Flexible threadpool for all netconf connectors, Max thread count is set to 4 -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
+ <name>global-netconf-processing-executor</name>
+ <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
+ <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
+ <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
+ <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ </threadFactory>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
+ <instance>
+ <name>global-netconf-dispatcher</name>
+ <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <instance>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <instance>
+ <name>global-netconf-processing-executor</name>
+ <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
+ </instance>
+ </service>
+ </services>
+
+ </data>
+ </configuration>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&revision=2014-04-08</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&revision=2013-12-01</capability>
+ </required-capabilities>
+</snapshot>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- 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
--->
-<snapshot>
- <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:md:sal:remote:rpc">prefix:remote-zeromq-rpc-server</type>
- <name>remoter</name>
- <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">5666</port>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
- <name>dom-broker</name>
- </dom-broker>
- </module>
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- </services>
- </data>
- </configuration>
-
- <required-capabilities>
- <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: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:common?module=opendaylight-md-sal-common&revision=2013-10-28</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc?module=odl-sal-dom-rpc-remote-cfg&revision=2013-10-28</capability>
- </required-capabilities>
-</snapshot>
-
</rpc-registry>
<data-broker>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<name>binding-data-broker</name>
</data-broker>
<configuration>
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-
- <!-- Netconf dispatcher to be used by all netconf-connectors -->
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">prefix:netconf-client-dispatcher</type>
- <name>global-netconf-dispatcher</name>
- <boss-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
- <name>global-boss-group</name>
- </boss-thread-group>
- <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
- <name>global-worker-group</name>
- </worker-thread-group>
- <timer xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
- <name>global-timer</name>
- </timer>
- </module>
-
- <!-- Netconf dispatcher to be used by all netconf-connectors -->
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
- <name>global-netconf-processing-executor-threadfactory</name>
- <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
- <name>global-netconf-processing-executor</name>
- <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
- <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
- <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
- <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
- <name>global-netconf-processing-executor-threadfactory</name>
- </threadFactory>
- </module>
-
<!-- Loopback connection to netconf server in controller using netconf-connector -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">prefix:sal-netconf-connector</type>
</processing-executor>
</module>
</modules>
-
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
- <instance>
- <name>global-netconf-dispatcher</name>
- <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
- <instance>
- <name>global-netconf-processing-executor-threadfactory</name>
- <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
- </instance>
- </service>
- <service>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
- <instance>
- <name>global-netconf-processing-executor</name>
- <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
- </instance>
- </service>
- </services>
-
</data>
</configuration>
<required-capabilities>
<capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&revision=2013-10-28</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&revision=2014-04-08</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&revision=2013-12-01</capability>
</required-capabilities>
</snapshot>
*/
package org.opendaylight.controller.sal.compatibility;
+import com.google.common.collect.Iterables;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
}
private boolean isKnownNodeConnector(final InstanceIdentifier<? extends Object> nodeConnectorIdentifier) {
- final List<PathArgument> path = nodeConnectorIdentifier.getPath();
- if (path.size() >= 3) {
- final PathArgument nodePath = path.get(1);
- final PathArgument nodeConnectorPath = path.get(2);
- final List<PathArgument> nodeConnectors = nodeToNodeConnectorsMap.get(nodePath);
- if (nodeConnectors != null) {
- return nodeConnectors.contains(nodeConnectorPath);
- }
+ final Iterator<PathArgument> it = nodeConnectorIdentifier.getPathArguments().iterator();
+
+ if (!it.hasNext()) {
+ return false;
}
- return false;
+ it.next();
+
+ if (!it.hasNext()) {
+ return false;
+ }
+ final PathArgument nodePath = it.next();
+
+ if (!it.hasNext()) {
+ return false;
+ }
+ final PathArgument nodeConnectorPath = it.next();
+
+ final List<PathArgument> nodeConnectors = nodeToNodeConnectorsMap.get(nodePath);
+ return nodeConnectors == null ? false :
+ nodeConnectors.contains(nodeConnectorPath);
}
private boolean recordNodeConnector(final InstanceIdentifier<? extends Object> nodeConnectorIdentifier) {
- final List<PathArgument> path = nodeConnectorIdentifier.getPath();
- if (path.size() < 3) {
+ final Iterator<PathArgument> it = nodeConnectorIdentifier.getPathArguments().iterator();
+
+ if (!it.hasNext()) {
return false;
}
+ it.next();
- final PathArgument nodePath = path.get(1);
- final PathArgument nodeConnectorPath = path.get(2);
+ if (!it.hasNext()) {
+ return false;
+ }
+ final PathArgument nodePath = it.next();
+
+ if (!it.hasNext()) {
+ return false;
+ }
+ final PathArgument nodeConnectorPath = it.next();
synchronized (this) {
List<PathArgument> nodeConnectors = this.nodeToNodeConnectorsMap.get(nodePath);
}
private List<PathArgument> removeNodeConnectors(final InstanceIdentifier<? extends Object> nodeIdentifier) {
- return this.nodeToNodeConnectorsMap.remove(nodeIdentifier.getPath().get(1));
+ return this.nodeToNodeConnectorsMap.remove(Iterables.get(nodeIdentifier.getPathArguments(), 1));
}
}
<!-- Compability Packages -->
<module>compatibility</module>
- <!-- Clustering -->
- <module>remoterpc-routingtable/implementation</module>
- <module>sal-remoterpc-connector/implementation</module>
<!-- Documentation -->
<module>sal-rest-docgen</module>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-parent</artifactId>
- <version>1.1-SNAPSHOT</version>
- <relativePath>../..</relativePath>
- </parent>
-
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- <packaging>bundle</packaging>
- <dependencies>
-
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services</artifactId>
- <version>${clustering.services.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-connector-api</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <scope>test</scope>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>${bundle.plugin.version}</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Export-Package>org.opendaylight.controller.sal.connector.remoterpc.api,
- org.opendaylight.controller.sal.connector.remoterpc.impl</Export-Package>
- <Import-Package>javax.xml.bind.annotation,
- org.opendaylight.controller.sal.core,
- org.opendaylight.controller.sal.utils,
- org.opendaylight.controller.sal.packet,
- org.opendaylight.controller.sal.topology,
- org.opendaylight.controller.clustering.services,
- org.opendaylight.controller.md.sal.common.api.data,
- org.opendaylight.yangtools.yang.binding,
- org.osgi.service.component,
- org.slf4j,
- org.apache.felix.dm,
- org.apache.commons.lang3.builder,
- org.apache.commons.lang3.tuple,
- org.eclipse.osgi.framework.console,
- org.osgi.framework,
- javax.transaction,
- com.google.common.base,
- com.google.common.collect</Import-Package>
- <Bundle-Activator>org.opendaylight.controller.sal.connector.remoterpc.impl.Activator</Bundle-Activator>
- </instructions>
- <manifestLocation>${project.basedir}/META-INF</manifestLocation>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <tag>HEAD</tag>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
- </scm>
-</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.sal.connector.remoterpc.api;
-
-import java.util.EventListener;
-
-public interface RouteChangeListener<I,R> extends EventListener {
-
-
- void onRouteUpdated(I key, R new_value);
-
- void onRouteDeleted(I key);
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.connector.remoterpc.api;
-
-import java.util.Set;
-
-public interface RoutingTable<I,R> {
-
- /**
- * 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 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 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.
- *
- *
- * @param routeId route identifier
- * @param route network address
- * @throws DuplicateRouteException
- * @throws RoutingTableException
- */
- 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;
-
-
- /**
- * Removes the network address for the route from routing table. If only
- * one network address existed, remove the route as well.
- * @param routeId
- * @param 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;
-
- /**
- * 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
- * @param routeId
- * @return
- */
- public Set<R> getRoutes(I routeId);
-
-
- /**
- * Returns the last inserted address from the list of network addresses
- * associated with the route.
- * @param routeId
- * @return
- */
- public R getLastAddedRoute(I routeId);
-
- public class DuplicateRouteException extends RoutingTableException {
- public DuplicateRouteException(String message) {
- super(message);
- }
-
- }
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.connector.remoterpc.api;
-
-/**
- * @author: syedbahm
- */
-public class RoutingTableException extends Exception {
-
- /**
- * Constructs a new exception with {@code null} as its detail message.
- * The cause is not initialized, and may subsequently be initialized by a
- * call to {@link #initCause}.
- */
- public RoutingTableException() {
- super();
- }
-
- /**
- * Constructs a new exception with the specified detail message. The
- * cause is not initialized, and may subsequently be initialized by
- * a call to {@link #initCause}.
- *
- * @param message the detail message. The detail message is saved for
- * later retrieval by the {@link #getMessage()} method.
- */
- public RoutingTableException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new exception with the specified detail message and
- * cause. <p>Note that the detail message associated with
- * {@code cause} is <i>not</i> automatically incorporated in
- * this exception's detail message.
- *
- * @param message the detail message (which is saved for later retrieval
- * by the {@link #getMessage()} method).
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A <tt>null</tt> value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- */
- public RoutingTableException(String message, Throwable cause) {
- super(message, cause);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-
-
-package org.opendaylight.controller.sal.connector.remoterpc.api;
-
-/**
- * @author: syedbahm
- *
- */
-public class SystemException extends Exception {
- /**
- * Constructs a new exception with {@code null} as its detail message.
- * The cause is not initialized, and may subsequently be initialized by a
- * call to {@link #initCause}.
- */
- public SystemException() {
- super();
- }
-
- /**
- * Constructs a new exception with the specified detail message. The
- * cause is not initialized, and may subsequently be initialized by
- * a call to {@link #initCause}.
- *
- * @param message the detail message. The detail message is saved for
- * later retrieval by the {@link #getMessage()} method.
- */
- public SystemException(String message) {
- super(message);
- }
-
- /**
- * Constructs a new exception with the specified detail message and
- * cause. <p>Note that the detail message associated with
- * {@code cause} is <i>not</i> automatically incorporated in
- * this exception's detail message.
- *
- * @param message the detail message (which is saved for later retrieval
- * by the {@link #getMessage()} method).
- * @param cause the cause (which is saved for later retrieval by the
- * {@link #getCause()} method). (A <tt>null</tt> value is
- * permitted, and indicates that the cause is nonexistent or
- * unknown.)
- * @since 1.4
- */
- public SystemException(String message, Throwable cause) {
- super(message, cause);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.connector.remoterpc.impl;
-
-import org.apache.felix.dm.Component;
-import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RouteChangeListener;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
-import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Dictionary;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Set;
-
-/**
- * @author: syedbahm
- */
-public class Activator extends ComponentActivatorAbstractBase {
-
- protected static final Logger logger = LoggerFactory
- .getLogger(Activator.class);
- private static final String CACHE_UPDATE_AWARE_REGISTRY_KEY = "cachenames" ;
-
-
- /**
- * Method which tells how many Global implementations are
- * supported by the bundle. This way we can tune the number of
- * components created. This components will be created ONLY at the
- * time of bundle startup and will be destroyed only at time of
- * bundle destruction, this is the major difference with the
- * implementation retrieved via getImplementations where all of
- * them are assumed to be in a container!
- *
- *
- * @return The list of implementations the bundle will support,
- * in Global version
- */
-
- @Override
- protected Object[] getGlobalImplementations(){
- logger.debug("Calling getGlobalImplementations to return:", RoutingTableImpl.class);
- return new Object[] {
- RoutingTableImpl.class
- };
- }
-
- /**
- * Configure the dependency for a given instance Global
- *
- * @param c Component assigned for this instance, this will be
- * what will be used for configuration
- * @param imp implementation to be configured
- *
- */
- @Override
- protected void configureGlobalInstance(Component c, Object imp){
- if (imp.equals(RoutingTableImpl.class)) {
- Dictionary<String, Set<String>> props = new Hashtable<String, Set<String>>();
- Set<String> propSet = new HashSet<String>();
- propSet.add(RoutingTableImpl.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);
- logger.debug("configureGlobalInstance adding dependency:", IClusterGlobalServices.class);
-
-
- // RouteChangeListener services will be none or many so the
- // dependency is optional
- c.add(createServiceDependency()
- .setService(RouteChangeListener.class)
- .setCallbacks("setRouteChangeListener", "unsetRouteChangeListener")
- .setRequired(false));
-
- //dependency is required as it provides us the caching support
- c.add(createServiceDependency().setService(
- IClusterGlobalServices.class).setCallbacks(
- "setClusterGlobalServices",
- "unsetClusterGlobalServices").setRequired(true));
-
- }
- }
-
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.connector.remoterpc.impl;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.transaction.HeuristicMixedException;
-import javax.transaction.HeuristicRollbackException;
-import javax.transaction.NotSupportedException;
-import javax.transaction.RollbackException;
-
-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.RoutingTable;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTableException;
-import org.opendaylight.controller.sal.connector.remoterpc.api.SystemException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-
-public class RoutingTableImpl<I, R> implements RoutingTable<I, R>, ICacheUpdateAware<I, R> {
-
- private final Logger log = LoggerFactory.getLogger(RoutingTableImpl.class);
-
- private IClusterGlobalServices clusterGlobalServices = null;
-
- private ConcurrentMap<I,R> globalRpcCache = null;
- private ConcurrentMap<I, LinkedHashSet<R>> rpcCache = null; //need routes to ordered by insert-order
-
- public static final String GLOBALRPC_CACHE = "remoterpc_routingtable.globalrpc_cache";
- public static final String RPC_CACHE = "remoterpc_routingtable.rpc_cache";
-
- public RoutingTableImpl() {
- }
-
- @Override
- public R getGlobalRoute(final I routeId) throws RoutingTableException, SystemException {
- Preconditions.checkNotNull(routeId, "getGlobalRoute: routeId cannot be null!");
- return globalRpcCache.get(routeId);
- }
-
- @Override
- public void addGlobalRoute(final I routeId, final 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(final 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(final I routeId) {
- Preconditions.checkNotNull(routeId, "getRoutes: routeId cannot be null!");
- Set<R> routes = rpcCache.get(routeId);
-
- if (routes == null) {
- return Collections.emptySet();
- }
-
- return ImmutableSet.copyOf(routes);
- }
-
-
-
- @Override
- public R getLastAddedRoute(final 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 void addRoute(final I routeId, final 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 void addRoutes(final Set<I> routeIds, final R route) throws RoutingTableException, SystemException {
- Preconditions.checkNotNull(routeIds, "addRoutes: routeIds must not be null");
- for (I routeId : routeIds){
- addRoute(routeId, route);
- }
- }
-
- @Override
- public void removeRoute(final I routeId, final 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;
- }
-
- try {
- log.debug("removeRoute: removing a new route with k/v [{}/{}]", routeId, route);
-
- 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);
- }
- }
-
- @Override
- public void removeRoutes(final Set<I> routeIds, final R route) throws RoutingTableException, SystemException {
- Preconditions.checkNotNull(routeIds, "removeRoutes: routeIds must not be null");
- for (I routeId : routeIds){
- removeRoute(routeId, route);
- }
- }
-
- /**
- * 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(final I routeId, final 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(final I routeId, final 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;
- }
-
- }
- //the method did not already return means it failed to remove route in 10 attempts
- throw new IllegalStateException("Failed to remove route [" + routeId + "]");
- }
-
-
- // /**
- // * @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(final IClusterGlobalServices clusterGlobalServices) {
- this.clusterGlobalServices = clusterGlobalServices;
- }
-
- public void unsetClusterGlobalServices(final 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
- */
-
- @SuppressWarnings("unchecked")
- 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);
- }
- }
-
- /**
- * 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
- */
-
- @SuppressWarnings("unchecked")
- 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;
- }
-
- //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(final 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<I, R> getGlobalRpcCache() {
- return this.globalRpcCache;
- }
-
- /**
- * Useful for unit testing <note>It has package
- * scope</note>
- */
- ConcurrentMap<I, LinkedHashSet<R>> 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 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();
- }
-
- 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(final I key, final String cacheName, final 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(final I key, final R new_value, final String cacheName, final 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(final I key, final String cacheName, final 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);
- // }
- // }
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.connector.remoterpc.impl;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.when;
-
-import java.net.URI;
-import java.util.EnumSet;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-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.RoutingTable;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
-public class RoutingTableImplTest {
-
- 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;
-
- @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 addGlobalRoute_ValidArguments_ShouldAdd() throws Exception {
-
- Assert.assertNotNull(mockGlobalRpcCache);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
-
- final String expectedRoute = "172.27.12.1:5000";
- routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
-
- ConcurrentMap latestCache = routingTable.getGlobalRpcCache();
- Assert.assertEquals(mockGlobalRpcCache, latestCache);
- Assert.assertEquals(expectedRoute, latestCache.get(routeIdentifier));
- }
-
- @Test (expected = RoutingTable.DuplicateRouteException.class)
- public void addGlobalRoute_DuplicateRoute_ShouldThrow() throws Exception{
-
- Assert.assertNotNull(mockGlobalRpcCache);
-
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
- routingTable.addGlobalRoute(routeIdentifier, new String());
- routingTable.addGlobalRoute(routeIdentifier, new String());
- }
-
- @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";
-
- routingTable.addGlobalRoute(routeIdentifier, expectedRoute);
-
- String actualRoute = routingTable.getGlobalRoute(routeIdentifier);
- Assert.assertEquals(expectedRoute, actualRoute);
- }
-
- @Test
- public void getGlobalRoute_NonExistentRouteId_ShouldReturnNull() throws Exception {
-
- Assert.assertNotNull(mockGlobalRpcCache);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
-
- String actualRoute = routingTable.getGlobalRoute(routeIdentifier);
- Assert.assertNull(actualRoute);
- }
-
- @Test
- public void removeGlobalRoute_ExistingRouteId_ShouldRemove() throws Exception {
-
- 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);
-
- routingTable.removeGlobalRoute(routeIdentifier);
- Assert.assertTrue(cache.size() == 0);
-
- }
-
- @Test
- public void removeGlobalRoute_NonExistentRouteId_ShouldDoNothing() throws Exception {
-
- Assert.assertNotNull(mockGlobalRpcCache);
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeIdentifier = getRouteIdentifier();
-
- ConcurrentMap cache = routingTable.getGlobalRpcCache();
- Assert.assertTrue(cache.size() == 0);
-
- routingTable.removeGlobalRoute(routeIdentifier);
- Assert.assertTrue(cache.size() == 0);
-
- }
-
- @Test
- public void addRoute_ForNewRouteId_ShouldAddRoute() throws Exception {
- Assert.assertTrue(mockRpcCache.size() == 0);
-
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
-
- routingTable.addRoute(routeId, new String());
- Assert.assertTrue(mockRpcCache.size() == 1);
-
- Set<String> routes = routingTable.getRoutes(routeId);
- Assert.assertEquals(1, routes.size());
- }
-
- @Test
- public void addRoute_ForExistingRouteId_ShouldAppendRoute() throws Exception {
-
- Assert.assertTrue(mockRpcCache.size() == 0);
-
- RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier> routeId = getRouteIdentifier();
-
- String route_1 = "10.0.0.1:5955";
- String route_2 = "10.0.0.2:5955";
-
- routingTable.addRoute(routeId, route_1);
- routingTable.addRoute(routeId, route_2);
-
- Assert.assertTrue(mockRpcCache.size() == 1);
-
- Set<String> routes = routingTable.getRoutes(routeId);
- Assert.assertEquals(2, routes.size());
- Assert.assertTrue(routes.contains(route_1));
- Assert.assertTrue(routes.contains(route_2));
- }
-
- @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();
- }
-
- Assert.assertEquals(2*numOfRoutesToAdd, routingTable.getRoutes(routeId).size());
- }
-
- @Test(expected = NullPointerException.class)
- public void addRoute_NullRouteId_shouldThrowNpe() throws Exception {
-
- routingTable.addRoute(null, new String());
- }
-
- @Test(expected = NullPointerException.class)
- public void addRoute_NullRoute_shouldThrowNpe() throws Exception{
-
- 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
-
- routes.add(new String()); //can not be modified; should throw
- }
-
- @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");
-
- Set<String> routes = routingTable.getRoutes(routeId); //returns Immutable Set
-
- Assert.assertEquals(2, routes.size());
- }
-
- @Test
- public void getLastAddedRoute_WhenMultipleRoutesExists_ShouldReturnLatestRoute()
- 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(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());
-
- }
-
- @Test
- public void removeRoute_WhenOnlyOneRouteExists_RemovesRouteId() throws Exception{
- Assert.assertNotNull(routingTable);
- RpcRouter.RouteIdentifier routeId = getRouteIdentifier();
- String route_1 = "10.0.0.1:5555";
-
- routingTable.addRoute(routeId, route_1);
- Assert.assertEquals(1, routingTable.getRoutes(routeId).size());
-
- 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();
- }
- }
- }
- };
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.integrationtest</artifactId>
- <version>0.5.1-SNAPSHOT</version>
- <relativePath>../../../commons/integrationtest</relativePath>
- </parent>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
- <tag>HEAD</tag>
- </scm>
-
- <artifactId>remoterpc-routingtable.integrationtest</artifactId>
- <version>0.4.1-SNAPSHOT</version>
-
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.it.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.stub</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-native</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-link-mvn</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.url</groupId>
- <artifactId>pax-url-aether</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
- </dependency>
- <dependency>
- <groupId>eclipselink</groupId>
- <artifactId>javax.resource</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-connector-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-native</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-link-mvn</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>antlr4-runtime-osgi-nohead</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>xtend-lib-osgi</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <version>${exam.version}</version>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam</artifactId>
- <version>${exam.version}</version>
- </dependency>
- </dependencies>
- <properties>
- <!-- Sonar jacoco plugin to get integration test coverage info -->
- <sonar.jacoco.reportPath>../implementation/target/jacoco.exec</sonar.jacoco.reportPath>
- <sonar.jacoco.itReportPath>../implementation/target/jacoco-it.exec</sonar.jacoco.itReportPath>
- </properties>
- <build>
- <plugins>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <configuration>
- <destFile>../implementation/target/jacoco-it.exec</destFile>
- <includes><include>org.opendaylight.controller.*</include></includes>
- </configuration>
- <executions>
- <execution>
- <id>pre-test</id>
- <goals>
- <goal>prepare-agent</goal>
- </goals>
- </execution>
- <execution>
- <id>post-test</id>
- <configuration>
- <skip>true</skip>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
+++ /dev/null
-/*
- * 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.connector.remoterpc.impl;
-
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.ops4j.pax.exam.Configuration;
-import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.util.PathUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import java.io.Serializable;
-import java.net.URI;
-import java.util.Set;
-
-import static org.ops4j.pax.exam.CoreOptions.junitBundles;
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.systemPackages;
-import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-
-
-
-
-@RunWith(PaxExam.class)
-public class
- ZeroMQRoutingTableTestIT {
- private Logger log = LoggerFactory
- .getLogger(ZeroMQRoutingTableTestIT.class);
-
- public static final String ODL = "org.opendaylight.controller";
- public static final String YANG = "org.opendaylight.yangtools";
- public static final String CONTROLLER = "org.opendaylight.controller";
- public static final String YANGTOOLS = "org.opendaylight.yangtools";
- RoutingIdentifierImpl rii = new RoutingIdentifierImpl();
- // get the OSGI bundle context
- @Inject
- private BundleContext bc;
- @Inject
- private RoutingTable routingTable = null;
-
- // Configure the OSGi container
- @Configuration
- public Option[] config() {
- return options(
- //
- systemProperty("logback.configurationFile").value(
- "file:" + PathUtils.getBaseDir()
- + "/src/test/resources/logback.xml"),
- // To start OSGi console for inspection remotely
- systemProperty("osgi.console").value("2401"),
- // Set the systemPackages (used by clustering)
- systemPackages("sun.reflect", "sun.reflect.misc", "sun.misc"),
- // List framework bundles
-
- mavenBundle("equinoxSDK381",
- "org.eclipse.equinox.console").versionAsInProject(),
- mavenBundle("equinoxSDK381",
- "org.eclipse.equinox.util").versionAsInProject(),
- mavenBundle("equinoxSDK381",
- "org.eclipse.osgi.services").versionAsInProject(),
- mavenBundle("equinoxSDK381",
- "org.eclipse.equinox.ds").versionAsInProject(),
- mavenBundle("equinoxSDK381",
- "org.apache.felix.gogo.command").versionAsInProject(),
- mavenBundle("equinoxSDK381",
- "org.apache.felix.gogo.runtime").versionAsInProject(),
- mavenBundle("equinoxSDK381",
- "org.apache.felix.gogo.shell").versionAsInProject(),
- // List logger bundles
- mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(),
- mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(),
- mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(),
- mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(),
- // List all the bundles on which the test case depends
- mavenBundle(ODL,
- "clustering.services").versionAsInProject(),
-
- mavenBundle(ODL, "sal").versionAsInProject(),
- mavenBundle(ODL,
- "sal.implementation").versionAsInProject(),
- mavenBundle(ODL, "containermanager").versionAsInProject(),
- mavenBundle(ODL,
- "containermanager.it.implementation").versionAsInProject(),
- mavenBundle("org.jboss.spec.javax.transaction",
- "jboss-transaction-api_1.1_spec").versionAsInProject(),
- mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),
- mavenBundle("org.apache.felix",
- "org.apache.felix.dependencymanager").versionAsInProject(),
- mavenBundle("org.apache.felix",
- "org.apache.felix.dependencymanager.shell").versionAsInProject(),
- mavenBundle("eclipselink", "javax.resource").versionAsInProject(),
-
- mavenBundle("com.google.guava","guava").versionAsInProject(),
- // List logger bundles
- mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(),
- mavenBundle("org.slf4j", "log4j-over-slf4j")
- .versionAsInProject(),
- mavenBundle("ch.qos.logback", "logback-core")
- .versionAsInProject(),
- mavenBundle("ch.qos.logback", "logback-classic")
- .versionAsInProject(),
-
- mavenBundle(ODL, "clustering.services")
- .versionAsInProject(),
- mavenBundle(ODL, "clustering.stub")
- .versionAsInProject(),
-
-
- // List all the bundles on which the test case depends
- mavenBundle(ODL, "sal")
- .versionAsInProject(),
- mavenBundle(ODL, "sal-connector-api")
- .versionAsInProject(),
- mavenBundle(ODL, "remoterpc-routingtable.implementation")
- .versionAsInProject(),
-
- mavenBundle("org.jboss.spec.javax.transaction",
- "jboss-transaction-api_1.1_spec").versionAsInProject(),
- mavenBundle("org.apache.commons", "commons-lang3")
- .versionAsInProject(),
- mavenBundle("org.apache.felix",
- "org.apache.felix.dependencymanager")
- .versionAsInProject(),
-
- mavenBundle(ODL,
- "sal-core-api")
- .versionAsInProject(),
- mavenBundle("org.opendaylight.yangtools","yang-data-api")
- .versionAsInProject(),
- mavenBundle("org.opendaylight.yangtools","yang-model-api")
- .versionAsInProject(),
- mavenBundle("org.opendaylight.yangtools","yang-binding")
- .versionAsInProject(),
-
- mavenBundle(CONTROLLER, "sal-binding-api").versionAsInProject(), //
- mavenBundle(CONTROLLER, "sal-binding-config").versionAsInProject(),
- mavenBundle(CONTROLLER, "sal-binding-broker-impl").versionAsInProject(), //
- mavenBundle("org.javassist", "javassist").versionAsInProject(), //
- mavenBundle(CONTROLLER, "sal-common-util").versionAsInProject(), //
-
- mavenBundle(YANGTOOLS, "yang-data-api").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-data-impl").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-model-api").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-model-util").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "yang-parser-api").versionAsInProject(),
- mavenBundle(YANGTOOLS, "yang-parser-impl").versionAsInProject(),
-
-
- mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "binding-model-api").versionAsInProject(), //
- mavenBundle(YANGTOOLS, "binding-generator-util").versionAsInProject(),
- mavenBundle(YANGTOOLS, "yang-parser-impl").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-type-provider").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-generator-api").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(),
- mavenBundle(YANGTOOLS, "binding-generator-impl").versionAsInProject(),
-
-
- mavenBundle(CONTROLLER, "sal-core-api").versionAsInProject().update(), //
- mavenBundle(CONTROLLER, "sal-broker-impl").versionAsInProject(), //
- mavenBundle(CONTROLLER, "sal-core-spi").versionAsInProject().update(), //
-
- mavenBundle(YANGTOOLS + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), //
-
- mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(),
- mavenBundle("com.google.guava", "guava").versionAsInProject(), //
- mavenBundle("org.javassist", "javassist").versionAsInProject(),
- mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), //
- mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), //
- mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), //
- mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), //
-
- mavenBundle(ODL, "sal-common").versionAsInProject(), //
- mavenBundle(ODL, "sal-common-api").versionAsInProject(),//
- mavenBundle(ODL, "sal-common-impl").versionAsInProject(), //
- mavenBundle(ODL, "sal-common-util").versionAsInProject(), //
-
- mavenBundle(ODL, "config-api").versionAsInProject(), //
- mavenBundle(ODL, "config-manager").versionAsInProject(), //
- mavenBundle("commons-io", "commons-io").versionAsInProject(),
- mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(),
-
- mavenBundle(ODL, "sal-binding-api").versionAsInProject(), //
- mavenBundle(ODL, "sal-binding-config").versionAsInProject(),
- mavenBundle("org.javassist", "javassist").versionAsInProject(), //
- mavenBundle(ODL, "sal-common-util").versionAsInProject(), //
-
- mavenBundle(YANG, "yang-data-api").versionAsInProject(), //
- mavenBundle(YANG, "yang-data-impl").versionAsInProject(), //
- mavenBundle(YANG, "yang-model-api").versionAsInProject(), //
- mavenBundle(YANG, "yang-model-util").versionAsInProject(), //
- mavenBundle(YANG, "yang-parser-api").versionAsInProject(),
- mavenBundle(YANG, "yang-parser-impl").versionAsInProject(),
-
-
- mavenBundle(YANG, "binding-generator-spi").versionAsInProject(), //
- mavenBundle(YANG, "binding-model-api").versionAsInProject(), //
- mavenBundle(YANG, "binding-generator-util").versionAsInProject(),
- mavenBundle(YANG, "yang-parser-impl").versionAsInProject(),
- mavenBundle(YANG, "binding-type-provider").versionAsInProject(),
- mavenBundle(YANG, "binding-generator-api").versionAsInProject(),
- mavenBundle(YANG, "binding-generator-spi").versionAsInProject(),
- mavenBundle(YANG, "binding-generator-impl").versionAsInProject(),
-
-
- mavenBundle(ODL, "sal-core-api").versionAsInProject().update(), //
- mavenBundle(ODL, "sal-broker-impl").versionAsInProject(), //
- mavenBundle(ODL, "sal-core-spi").versionAsInProject().update(), //
-
- mavenBundle(YANG + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), //
-
- mavenBundle(YANG, "concepts").versionAsInProject(),
- mavenBundle(YANG, "yang-binding").versionAsInProject(), //
- mavenBundle(YANG, "yang-common").versionAsInProject(), //
- mavenBundle(YANG+".thirdparty", "xtend-lib-osgi").versionAsInProject(),
- mavenBundle("com.google.guava", "guava").versionAsInProject(), //
- mavenBundle("org.javassist", "javassist").versionAsInProject(),
-
- junitBundles());
- }
-
- private String stateToString(int state) {
- switch (state) {
- case Bundle.ACTIVE:
- return "ACTIVE";
- case Bundle.INSTALLED:
- return "INSTALLED";
- case Bundle.RESOLVED:
- return "RESOLVED";
- case Bundle.UNINSTALLED:
- return "UNINSTALLED";
- default:
- return "Not CONVERTED";
- }
- }
-
- @Test
- public void testAddGlobalRoute () throws Exception{
-
- routingTable.addGlobalRoute(rii,"172.27.12.1:5000");
-
- Set<String> routes = routingTable.getRoutes(rii);
-
- for(String route:routes){
- Assert.assertEquals(route,"172.27.12.1:5000");
- }
-
-
- }
-
-
- @Test
- public void testDeleteGlobalRoute () throws Exception{
-
- routingTable.removeGlobalRoute(rii);
-
- Set<String> routes = routingTable.getRoutes(rii);
-
- Assert.assertNull(routes);
-
-
- }
-
-
-
- class RoutingIdentifierImpl implements RpcRouter.RouteIdentifier,Serializable {
-
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace,"global");
- private final QName instance = new QName(URI.create("127.0.0.1"),"local");
-
- @Override
- public QName getContext() {
- return QNAME;
- }
-
- @Override
- public QName getType() {
- return QNAME;
- }
-
- @Override
- public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getRoute() {
- return InstanceIdentifier.of(instance);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- RoutingIdentifierImpl that = (RoutingIdentifierImpl) o;
-
- if (QNAME != null ? !QNAME.equals(that.QNAME) : that.QNAME != null) return false;
- if (instance != null ? !instance.equals(that.instance) : that.instance != null) return false;
- if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = namespace != null ? namespace.hashCode() : 0;
- result = 31 * result + (QNAME != null ? QNAME.hashCode() : 0);
- result = 31 * result + (instance != null ? instance.hashCode() : 0);
- return result;
- }
- }
-
-
-
-
-
-}
+++ /dev/null
-<configuration scan="true">
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
- </pattern>
- </encoder>
- </appender>
-
- <root level="error">
- <appender-ref ref="STDOUT" />
- </root>
-</configuration>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>sal-remoterpc-connector-test-parent</artifactId>
- <groupId>org.opendaylight.controller.tests</groupId>
- <version>1.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>remoterpc-routingtable-nb-it</artifactId>
- <packaging>bundle</packaging>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>${bundle.plugin.version}</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Export-Package>
- org.opendaylight.controller.tests.zmqroutingtable.rest
- </Export-Package>
- <Import-Package>
- com.sun.jersey.spi.container.servlet,
- com.fasterxml.jackson.annotation,
- 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.opendaylight.controller.sal.utils,
- org.opendaylight.yangtools.yang.common,
- org.opendaylight.controller.sal.connector.api,
- org.opendaylight.controller.sal.connector.remoterpc.api,
- org.opendaylight.controller.sal.connector.remoterpc.impl,
- org.osgi.framework,
- com.google.common.base,
- org.opendaylight.yangtools.yang.data.api,
- !org.codehaus.enunciate.jaxrs
-
- </Import-Package>
- <Web-ContextPath>/controller/nb/v2/zmqnbrt</Web-ContextPath>
- <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
- </instructions>
- <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- </dependencies>
-
- </project>
+++ /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.tests.zmqroutingtable.rest;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
-import java.io.Serializable;
-import java.net.URI;
-
-/**
- * @author: syedbahm
- * Date: 12/10/13
- */
-public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier, Serializable {
-
- private final URI namespace;
- private final QName QNAME;
- private final QName instance;
-
- public RouteIdentifierImpl() {
- namespace = URI.create("http://cisco.com/example");
- QNAME = new QName(namespace, "global");
- instance = new QName(URI.create("127.0.0.1"), "local");
- }
-
- public RouteIdentifierImpl(String url,String instanceIP){
- namespace = URI.create(url);
- QNAME = new QName(namespace,"global");
- instance = new QName(URI.create(instanceIP), "local");
- }
-
-
- @Override
- public QName getContext() {
- return QNAME;
- }
-
- @Override
- public QName getType() {
- return QNAME;
- }
-
- @Override
- public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getRoute() {
- return InstanceIdentifier.of(instance);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- RouteIdentifierImpl that = (RouteIdentifierImpl) o;
-
- if (!QNAME.equals(that.QNAME)) return false;
- if (!instance.equals(that.instance)) return false;
- if (!namespace.equals(that.namespace)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = namespace.hashCode();
- result = 31 * result + QNAME.hashCode();
- result = 31 * result + instance.hashCode();
- return result;
- }
-}
+++ /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.tests.zmqroutingtable.rest;
-
-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.impl.RoutingTableImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleReference;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import java.io.Serializable;
-import java.net.URI;
-
-@Path("router")
-public class Router implements Serializable {
- private Logger _logger = LoggerFactory.getLogger(Router.class);
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
-
-
- @GET
- @Path("/hello")
- @Produces(MediaType.TEXT_PLAIN)
- public String hello() {
- return "Hello";
- }
-
-
-
-
- @GET
- @Path("/rtadd")
- @Produces(MediaType.TEXT_PLAIN)
- public String addToRoutingTable(@QueryParam("nsp") String namespace,@QueryParam("inst") String instance,@QueryParam("port") String port) {
- _logger.info("Invoking adding an entry in routing table");
-
- BundleContext ctx = getBundleContext();
- ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class);
- if (routingTableServiceReference == null) {
- _logger.debug("Could not get routing table impl reference");
- return "Could not get routingtable referen ";
- }
- RoutingTableImpl routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference);
- if (routingTable == null) {
- _logger.info("Could not get routing table service");
- return "Could not get routing table service";
- }
-
-
- RouteIdentifierImpl rii = new RouteIdentifierImpl(namespace,instance);
- try {
- routingTable.addGlobalRoute(rii, instance+":"+ port);
- } catch (RoutingTableException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
-
- } catch (SystemException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
- }
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Result of adding route:").append("\n")
- .append(routingTable.dumpRoutingTableCache());
- return stringBuilder.toString();
- }
-
- @GET
- @Path("/rtdelete")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeDeleteRoutingTable(@QueryParam("nsp") String namespace,@QueryParam("inst") String instance) {
- _logger.info("Invoking delete an entry in routing table");
-
- BundleContext ctx = getBundleContext();
- ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class);
- if (routingTableServiceReference == null) {
- _logger.debug("Could not get routing table impl reference");
- return "Could not get routingtable referen ";
- }
- RoutingTableImpl routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference);
- if (routingTable == null) {
- _logger.info("Could not get routing table service");
- return "Could not get routing table service";
- }
-
-
- RouteIdentifierImpl rii = new RouteIdentifierImpl(namespace,instance);
- try {
- routingTable.removeGlobalRoute(rii);
- } catch (RoutingTableException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
-
- } catch (SystemException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
- }
-
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Result of deleting route:").append("\n")
- .append(routingTable.dumpRoutingTableCache());
-
- return stringBuilder.toString();
- }
-
- @GET
- @Path("/routingtable")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeGetRoutingTable() {
- _logger.info("Invoking getting of routing table");
-
- BundleContext ctx = getBundleContext();
- ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class);
- if (routingTableServiceReference == null) {
- _logger.debug("Could not get routing table impl reference");
- return "Could not get routingtable referen ";
- }
- RoutingTableImpl routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference);
- if (routingTable == null) {
- _logger.info("Could not get routing table service");
- return "Could not get routing table service";
- }
-
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Result of getting routetable:").append("\n")
- .append(routingTable.dumpRoutingTableCache());
-
- return stringBuilder.toString();
- }
-
-
-
- private BundleContext getBundleContext() {
- ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
- Bundle bundle = null;
-
- if (tlcl instanceof BundleReference) {
- bundle = ((BundleReference) tlcl).getBundle();
- } else {
- _logger.info("Unable to determine the bundle context based on " +
- "thread context classloader.");
- bundle = FrameworkUtil.getBundle(this.getClass());
- }
- return (bundle == null ? null : bundle.getBundleContext());
- }
-
-
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
- version="3.0">
- <servlet>
- <servlet-name>JAXRSZmqRT</servlet-name>
- <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
- <init-param>
- <param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>JAXRSZmqRT</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
-
-
-
- <security-constraint>
- <web-resource-collection>
- <web-resource-name>NB api</web-resource-name>
- <url-pattern>/*</url-pattern>
- <http-method>POST</http-method>
- <http-method>GET</http-method>
- <http-method>PUT</http-method>
- <http-method>PATCH</http-method>
- <http-method>DELETE</http-method>
- <http-method>HEAD</http-method>
- </web-resource-collection>
- <auth-constraint>
- <role-name>System-Admin</role-name>
- <role-name>Network-Admin</role-name>
- <role-name>Network-Operator</role-name>
- <role-name>Container-User</role-name>
- </auth-constraint>
- </security-constraint>
-
- <security-role>
- <role-name>System-Admin</role-name>
- </security-role>
- <security-role>
- <role-name>Network-Admin</role-name>
- </security-role>
- <security-role>
- <role-name>Network-Operator</role-name>
- </security-role>
- <security-role>
- <role-name>Container-User</role-name>
- </security-role>
-
- <login-config>
- <auth-method>BASIC</auth-method>
- <realm-name>opendaylight</realm-name>
- </login-config>
-</web-app>
--- /dev/null
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface BindingTransactionChain extends TransactionChain<InstanceIdentifier<?>, DataObject> {
+
+ @Override
+ ReadOnlyTransaction newReadOnlyTransaction();
+
+ @Override
+ ReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ WriteTransaction newWriteOnlyTransaction();
+
+}
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
* <p>
* For more information on usage, please see the documentation in {@link AsyncDataBroker}.
*/
-public interface DataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>, BindingService {
+public interface DataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>, BindingService, TransactionChainFactory<InstanceIdentifier<?>, DataObject> {
@Override
ReadOnlyTransaction newReadOnlyTransaction();
void unregisterFunctionality(ProviderFunctionality functionality);
}
+ /**
+ * Represents an RPC implementation registration. Users should call the
+ * {@link ObjectRegistration#close close} method when the registration is no longer needed.
+ *
+ * @param <T> the implemented RPC service interface
+ */
public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
+ /**
+ * Returns the implemented RPC service interface.
+ */
Class<T> getServiceType();
}
+ /**
+ * Represents a routed RPC implementation registration. Users should call the
+ * {@link RoutedRegistration#close close} method when the registration is no longer needed.
+ *
+ * @param <T> the implemented RPC service interface
+ */
public interface RoutedRpcRegistration<T extends RpcService> extends RpcRegistration<T>,
RoutedRegistration<Class<? extends BaseIdentity>, InstanceIdentifier<?>, T> {
import org.opendaylight.yangtools.yang.binding.RpcService;
/**
- * Base interface defining contract for retrieving MD-SAL
- * version of RpcServices
+ * Provides access to registered Remote Procedure Call (RPC) service implementations. The RPCs are
+ * defined in YANG models.
+ * <p>
+ * RPC implementations are registered using the {@link RpcProviderRegistry}.
*
*/
public interface RpcConsumerRegistry extends BindingAwareService {
/**
- * Returns a session specific instance (implementation) of requested
- * YANG module implementation / service provided by consumer.
+ * Returns an implementation of a requested RPC service.
*
- * @return Session specific implementation of service
+ * <p>
+ * The returned instance is not an actual implementation of the RPC service
+ * interface, but a proxy implementation of the interface that forwards to
+ * an actual implementation, if any.
+ * <p>
+ *
+ * The following describes the behavior of the proxy when invoking RPC methods:
+ * <ul>
+ * <li>If an actual implementation is registered with the MD-SAL, all invocations are
+ * forwarded to the registered implementation.</li>
+ * <li>If no actual implementation is registered, all invocations will fail by
+ * throwing {@link IllegalStateException}.</li>
+ * <li>Prior to invoking the actual implementation, the method arguments are are validated.
+ * If any are invalid, an {@link IllegalArgumentException} is thrown.
+ * </ul>
+ *
+ * The returned proxy is automatically updated with the most recent
+ * registered implementation.
+ * <p>
+ * The generated RPC method APIs require implementors to return a {@link java.util.concurrent.Future Future}
+ * instance that wraps the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}. Since
+ * RPC methods may be implemented asynchronously, callers should avoid blocking on the
+ * {@link java.util.concurrent.Future Future} result. Instead, it is recommended to use
+ * {@link com.google.common.util.concurrent.JdkFutureAdapters#listenInPoolThread(java.util.concurrent.Future)}
+ * or {@link com.google.common.util.concurrent.JdkFutureAdapters#listenInPoolThread(java.util.concurrent.Future, java.util.concurrent.Executor)}
+ * to listen for Rpc Result. This will asynchronously listen for future result in executor and
+ * will not block current thread.
+ *
+ * <pre>
+ * final Future<RpcResult<SomeRpcOutput>> future = someRpcService.someRpc( ... );
+ * Futures.addCallback(JdkFutureAdapters.listenInThreadPool(future), new FutureCallback<RpcResult<SomeRpcOutput>>() {
+ *
+ * public void onSuccess(RpcResult<SomeRpcOutput> result) {
+ * // process result ...
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * // RPC failed
+ * }
+ * );
+ * </pre>
+ * @param serviceInterface the interface of the RPC Service. Typically this is an interface generated
+ * from a YANG model.
+ * @return the proxy for the requested RPC service. This method never returns null.
*/
- <T extends RpcService> T getRpcService(Class<T> module);
+ <T extends RpcService> T getRpcService(Class<T> serviceInterface);
}
import org.opendaylight.yangtools.yang.binding.RpcService;
/**
- * Interface defining provider's access to the Rpc Registry which could be used
- * to register their implementations of service to the MD-SAL.
+ * Provides a registry for Remote Procedure Call (RPC) service implementations. The RPCs are
+ * defined in YANG models.
+ * <p>
+ * There are 2 types of RPCs:
+ * <ul>
+ * <li>Global</li>
+ * <li>Routed</li>
+ * </ul>
*
- * @author ttkacik
+ * <h2>Global RPC</h2>
+ * <p>
+ * An RPC is global if there is intended to be only 1 registered implementation. A global RPC is not
+ * explicitly declared as such, essentially any RPC that is not defined to be routed is considered global.
+ * <p>
+ * Global RPCs are registered using the
+ * {@link #addRpcImplementation(Class, RpcService)} method.
*
+ * <h2>Routed RPC</h2>
+ * <p>
+ * MD-SAL supports routing of RPC between multiple implementations where the appropriate
+ * implementation is selected at run time based on the content of the RPC message as described in
+ * YANG model.
+ * <p>
+ * RPC routing is based on:
+ * <ul>
+ * <li><b>Route identifier</b> -
+ * An {@link org.opendaylight.yangtools.yang.binding.InstanceIdentifier InstanceIdentifier} value
+ * which is part of the RPC input. This value is used to select the correct
+ * implementation at run time.</li>
+ * <li><b>Context Type</b> - A YANG-defined construct which constrains the subset of
+ * valid route identifiers for a particular RPC.</li>
+ * </ul>
+ *
+ * <h3>Context type</h3>
+ * <p>
+ * A context type is modeled in YANG using a combination of a YANG <code>identity</code>
+ * and Opendaylight specific extensions from <code>yang-ext</code> module. These extensions are:
+ * <ul>
+ * <li><b>context-instance</b> - This is used in the data tree part of a YANG model to
+ * define a context type that associates nodes with a specified context <code>identity</code>.
+ * Instance identifiers that reference these nodes are valid route identifiers for RPCs that
+ * reference this context type.</li>
+ * <li><b>context-reference</b> - This is used in RPC input to mark a leaf of type
+ * <code>instance-identifier</code> as a reference to the particular context type defined by the
+ * specified context <code>identity</code>. The value of this
+ * leaf is used by the RPC broker at run time to route the RPC request to the correct implementation.
+ * Note that <code>context-reference</code> may only be used on leaf elements of type
+ * <code>instance-identifier</code> or a type derived from <code>instance-identifier</code>.</li>
+ * </ul>
+ *
+ *
+ * <h3>Routed RPC example</h3>
+ * <p>
+ * <h5>1. Defining a Context Type</h5>
+ * <p>
+ * The following snippet declares a simple YANG <code>identity</code> named <code>example-context</code>:
+ *
+ * <pre>
+ * module example {
+ * ...
+ * identity example-context {
+ * description "Identity used to define an example-context type";
+ * }
+ * ...
+ * }
+ * </pre>
+ * <p>
+ * We then use the declared identity to define a context type by using it in combination
+ * with the <code>context-instance</code> YANG extension. We'll associate the context type
+ * with a list element in the data tree. This defines the set of nodes whose instance
+ * identifiers are valid for the <code>example-context</code> context type.
+ * <p>
+ * The following YANG snippet imports the <code>yang-ext</code> module and defines the list
+ * element named <code>item</code> inside a container named <code>foo</code>:
+ *
+ * <pre>
+ * module foo {
+ * ...
+ * import yang-ext {prefix ext;}
+ * ...
+ * container foo {
+ * list item {
+ * key "id";
+ * leaf id {type string;}
+ * ext:context-instance "example-context";
+ * }
+ * }
+ * ...
+ * }
+ * </pre>
+ * <p>
+ * The statement <code>ext:context-instance "example-context";</code> inside the list element
+ * declares that any instance identifier referencing <code>item</code> in the data
+ * tree is valid for <code>example-context</code>. For example, the following instance
+ * identifier:
+ * <pre>
+ * InstanceIdentifier.create(Foo.class).child(Item.class,new ItemKey("Foo"))
+ * </pre>
+ * is valid for <code>example-context</code>. However the following:
+ * <pre>
+ * InstanceIdentifier.create(Example.class)
+ * </pre>
+ * is not valid.
+ * <p>
+ * So using an <code>identity</code> in combination with <code>context-instance</code> we
+ * have effectively defined a context type that can be referenced in a YANG RPC input.
+ *
+ * <h5>2. Defining an RPC to use the Context Type</h5>
+ * <p>
+ * To define an RPC to be routed based on the context type we need to add an input leaf element
+ * that references the context type which will hold an instance identifier value to be
+ * used to route the RPC.
+ * <p>
+ * The following snippet defines an RPC named <code>show-item</code> with 2 leaf elements
+ * as input: <code>item</code> of type <code>instance-identifier</code> and <code>description</code>:
+ *
+ * <pre>
+ * module foo {
+ * ...
+ * import yang-ext {prefix ext;}
+ * ...
+ * rpc show-item {
+ * input {
+ * leaf item {
+ * type instance-identifier;
+ * ext:context-reference example-context;
+ * }
+ * leaf description {
+ * type "string";
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ * <p>
+ * We mark the <code>item</code> leaf with a <code>context-reference</code> statement that
+ * references the <code>example-context</code> context type. RPC calls will then be routed
+ * based on the instance identifier value contained in <code>item</code>. Only instance
+ * identifiers that point to a <code>foo/item</code> node are valid as input.
+ * <p>
+ * The generated RPC Service interface for the module is:
+ *
+ * <pre>
+ * interface FooService implements RpcService {
+ * Future<RpcResult<Void>> showItem(ShowItemInput input);
+ * }
+ * </pre>
+ * <p>
+ * For constructing the RPC input, there are generated classes ShowItemInput and ShowItemInputBuilder.
+ *
+ * <h5>3. Registering a routed RPC implementation</h5>
+ * <p>
+ * To register a routed implementation for the <code>show-item</code> RPC, we must use the
+ * {@link #addRoutedRpcImplementation(Class, RpcService)} method. This
+ * will return a {@link RoutedRpcRegistration} instance which can then be used to register /
+ * unregister routed paths associated with the registered implementation.
+ * <p>
+ * The following snippet registers <code>myImpl</code> as the RPC implementation for an
+ * <code>item</code> with key <code>"foo"</code>:
+ * <pre>
+ * // Create the instance identifier path for item "foo"
+ * InstanceIdentifier path = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("foo"));
+ *
+ * // Register myImpl as the implementation for the FooService RPC interface
+ * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
+ *
+ * // Now register for the context type and specific path ID. The context type is specified by the
+ * // YANG-generated class for the example-context identity.
+ * reg.registerPath(ExampleContext.class, path);
+ * </pre>
+ * <p>
+ * It is also possible to register the same implementation for multiple paths:
+ *
+ * <pre>
+ * InstanceIdentifier one = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("One"));
+ * InstanceIdentifier two = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("Two"));
+ *
+ * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
+ * reg.registerPath(ExampleContext.class, one);
+ * reg.registerPath(ExampleContext.class, two);
+ * </pre>
+ *
+ * <p>
+ * When another client invokes the <code>showItem(ShowItemInput)</code> method on the proxy instance
+ * retrieved via {@link RpcConsumerRegistry#getRpcService(Class)}, the proxy will inspect the
+ * arguments in ShowItemInput, extract the InstanceIdentifier value of the <code>item</code> leaf and select
+ * the implementation whose registered path matches the InstanceIdentifier value of the <code>item</code> leaf.
+ *
+ * <h2>Notes for RPC Implementations</h2>
+ *
+ * <h3>RpcResult</h3>
+ * <p>
+ * The generated interfaces require implementors to return
+ * {@link java.util.concurrent.Future Future}<{@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}<{RpcName}Output>> instances.
+ *
+ * Implementations should do processing of RPC calls asynchronously and update the
+ * returned {@link java.util.concurrent.Future Future} instance when processing is complete.
+ * However using {@link com.google.common.util.concurrent.Futures#immediateFuture(Object) Futures.immediateFuture}
+ * is valid only if the result is immediately available and asynchronous processing is unnecessary and
+ * would only introduce additional complexity.
+ *
+ * <p>
+ * The {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult} is a generic
+ * wrapper for the RPC output payload, if any, and also allows for attaching error or
+ * warning information (possibly along with the payload) should the RPC processing partially
+ * or completely fail. This is intended to provide additional human readable information
+ * for users of the API and to transfer warning / error information across the system
+ * so it may be visible via other external APIs such as Restconf.
+ * <p>
+ * It is recommended to use the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}
+ * for conveying appropriate error information
+ * on failure rather than purposely throwing unchecked exceptions if at all possible.
+ * While unchecked exceptions will fail the returned {@link java.util.concurrent.Future Future},
+ * using the intended RpcResult to convey the error information is more user-friendly.
*/
public interface RpcProviderRegistry extends //
RpcConsumerRegistry, //
RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
/**
- * Registers a global RpcService implementation.
+ * Registers a global implementation of the provided RPC service interface.
+ * All methods of the interface are required to be implemented.
+ *
+ * @param serviceInterface the YANG-generated interface of the RPC Service for which to register.
+ * @param implementation "the implementation of the RPC service interface.
+ * @return an RpcRegistration instance that should be used to unregister the RPC implementation
+ * when no longer needed by calling {@link RpcRegistration#close()}.
*
- * @param type
- * @param implementation
- * @return
+ * @throws IllegalStateException
+ * if the supplied RPC interface is a routed RPC type.
*/
- <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> serviceInterface, T implementation)
throws IllegalStateException;
/**
+ * Registers an implementation of the given routed RPC service interface.
+ * <p>
+ * See the {@link RpcProviderRegistry class} documentation for information and example on
+ * how to use routed RPCs.
*
- * Register a Routed RpcService where routing is determined on annotated
- * (in YANG model) context-reference and value of annotated leaf.
- *
- * @param type
- * Type of RpcService, use generated interface class, not your
- * implementation class
- * @param implementation
- * Implementation of RpcService
- * @return Registration object for routed Rpc which could be used to unregister
+ * @param serviceInterface the YANG-generated interface of the RPC Service for which to register.
+ * @param implementation the implementation instance to register.
+ * @return a RoutedRpcRegistration instance which can be used to register paths for the RPC
+ * implementation via invoking {@link RoutedRpcRegistration#registerPath(....).
+ * {@link RoutedRpcRegistration#close()} should be called to unregister the implementation
+ * and all previously registered paths when no longer needed.
*
* @throws IllegalStateException
+ * if the supplied RPC interface is not a routed RPC type.
*/
- <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type, T implementation)
+ <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> serviceInterface,
+ T implementation)
throws IllegalStateException;
}
*
*
* @see DataProviderService
+ * @deprecated Replaced by newer better documented version {@link org.opendaylight.controller.md.sal.binding.api.DataBroker}
*/
+@Deprecated
public interface DataBrokerService extends //
BindingAwareService, //
DataModificationTransactionFactory<InstanceIdentifier<? extends DataObject>, DataObject>, //
* Creates a data modification transaction.
*
* @return new blank data modification transaction.
+ * @deprecated Replaced by more specific transaction types. Please use
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ * or
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
*/
+ @Deprecated
@Override
DataModificationTransaction beginTransaction();
/**
* 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)-
+ * inject state into providers. E.g. Flow configuration)
+ *
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction()}
*
*/
+ @Deprecated
@Override
public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path);
* (Store which is populated by providers, which is usually used to
* capture state of providers. E.g. Topology)
*
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction()}
*/
+ @Deprecated
@Override
public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path);
*
* Callback is invoked each time data in subtree changes.
*
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#registerDataChangeListener(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier, org.opendaylight.controller.md.sal.binding.api.DataChangeListener, org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope)}
+ * which provides more fine-grained registration options.
*/
+ @Deprecated
@Override
public ListenerRegistration<DataChangeListener> registerDataChangeListener(
InstanceIdentifier<? extends DataObject> path, DataChangeListener listener);
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+/**
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.binding.api.DataChangeListener}
+ *
+ */
+@Deprecated
public interface DataChangeListener extends
org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<InstanceIdentifier<? extends DataObject>, DataObject> {
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
+/**
+ *
+ *
+ * @deprecated Replaced by more specific transaction types. Please use
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ * or
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ *
+ *
+ */
+@Deprecated
public interface DataModificationTransaction extends DataModification<InstanceIdentifier<? extends DataObject>, DataObject> {
/**
* Returns an unique identifier for transaction
/**
* DataProviderService is common access point for {@link org.opendaylight.controller.sal.binding.api.BindingAwareProvider} providers
* to access data trees described by the YANG model.
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.common.api.data.AsyncConfigurationCommitCoordinator} service.
*/
+@Deprecated
public interface DataProviderService extends DataBrokerService, DataProvisionService<InstanceIdentifier<? extends DataObject>, DataObject> {
/**
* Registers a data reader for particular subtree of overal YANG data tree.
* @param path Subpath which is handled by registered data reader
* @param reader Instance of reader which
* @return Registration object for reader. Invoking {@link Registration#close()} will unregister reader.
+ * @deprecated Data Reader contract is removed from capabilities of MD-SAL and is replaced by replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
*/
+ @Deprecated
Registration<DataReader<InstanceIdentifier<? extends DataObject>,DataObject>> registerDataReader(InstanceIdentifier<? extends DataObject> path,DataReader<InstanceIdentifier<? extends DataObject>,DataObject> reader);
}
* Trigger for refreshing of the data exposed by the {@link Provider}
*
*
- *
+ * @deprecated Unused, not supported. Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore.
*/
+@Deprecated
public interface DataRefresher extends BindingAwareProvider.ProviderFunctionality {
/**
/**
* Utility interface which does type capture for BindingAware DataReader.
*
- * @author
+ *
+ * @deprecated Removed, replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore.
*
*/
+@Deprecated
public interface RuntimeDataProvider extends ProviderFunctionality,DataReader<InstanceIdentifier<? extends DataObject>, DataObject> {
*
* To get instance of synchronized wrapper use {@link #from(DataModificationTransaction)}
*
+ * @deprecated Replaced by more specific transaction types. Please use
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ * or
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ *
*/
+@Deprecated
public final class SynchronizedTransaction implements DataModificationTransaction,Delegator<DataModificationTransaction> {
private final DataModificationTransaction delegate;
- private SynchronizedTransaction(DataModificationTransaction delegate) {
+ private SynchronizedTransaction(final DataModificationTransaction delegate) {
this.delegate = delegate;
}
* @param transaction Transaction for which synchronized wrapper should be created.
* @return Synchronized wrapper over transaction.
*/
- public static final SynchronizedTransaction from(DataModificationTransaction transaction) {
+ public static final SynchronizedTransaction from(final DataModificationTransaction transaction) {
Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
if (transaction instanceof SynchronizedTransaction) {
return (SynchronizedTransaction) transaction;
}
@Override
- public synchronized DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
+ public synchronized DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
return delegate.readOperationalData(path);
}
}
@Override
- public synchronized DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
+ public synchronized DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
return delegate.readConfigurationData(path);
}
}
@Override
- public synchronized void putOperationalData(InstanceIdentifier<? extends DataObject> path, DataObject data) {
+ public synchronized void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
delegate.putOperationalData(path, data);
}
@Override
- public synchronized void putConfigurationData(InstanceIdentifier<? extends DataObject> path, DataObject data) {
+ public synchronized void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
delegate.putConfigurationData(path, data);
}
}
@Override
- public synchronized void removeOperationalData(InstanceIdentifier<? extends DataObject> path) {
+ public synchronized void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
delegate.removeOperationalData(path);
}
@Override
- public synchronized void removeConfigurationData(InstanceIdentifier<? extends DataObject> path) {
+ public synchronized void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
delegate.removeConfigurationData(path);
}
}
@Override
- public synchronized ListenerRegistration<DataTransactionListener> registerListener(DataTransactionListener listener) {
+ public synchronized ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
return delegate.registerListener(listener);
}
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
-import org.osgi.framework.BundleContext;
public class BindingAsyncDataBrokerImplModule extends
org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModule implements
Provider {
- private BundleContext bundleContext;
public BindingAsyncDataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
// FIXME: Switch this to DOM Broker registration which would not require
// BundleContext when API are updated.
- ProviderSession session = domBroker.registerProvider(this, getBundleContext());
+ ProviderSession session = domBroker.registerProvider(this, null);
DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
SchemaService schemaService = session.getService(SchemaService.class);
return new ForwardedBindingDataBroker(domDataBroker, mappingService, schemaService);
}
- // FIXME: Remove this when DOM Broker registration would not require
- // BundleContext
- @Deprecated
- private BundleContext getBundleContext() {
- return bundleContext;
- }
- // FIXME: Remove this when DOM Broker registration would not require
- // BundleContext
- @Deprecated
- void setBundleContext(final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
+
+
@Override
public Collection<ProviderFunctionality> getProviderFunctionality() {
*/
package org.opendaylight.controller.config.yang.md.sal.binding.impl;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.osgi.framework.BundleContext;
public class BindingAsyncDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModuleFactory {
-
-
-
- @Override
- public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName,
- final DependencyResolver dependencyResolver, final BindingAsyncDataBrokerImplModule oldModule,
- final AutoCloseable oldInstance, final BundleContext bundleContext) {
- BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, oldModule, oldInstance, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName,
- final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
- // TODO Auto-generated method stub
- BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
}
-/*\r
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-/**\r
- * Generated file\r
-\r
- * Generated from: yang module name: opendaylight-sal-binding-broker-impl yang module local name: binding-broker-impl\r
- * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator\r
- * Generated at: Wed Nov 20 17:33:01 CET 2013\r
- *\r
- * Do not modify this file unless it is present under src/main directory\r
- */\r
-package org.opendaylight.controller.config.yang.md.sal.binding.impl;\r
-\r
-import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;\r
-import org.opendaylight.controller.sal.binding.impl.RootBindingAwareBroker;\r
-import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;\r
-import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBindingBrokerImpl;\r
-import org.opendaylight.controller.sal.binding.impl.forward.DomForwardingUtils;\r
-import org.osgi.framework.BundleContext;\r
-\r
-/**\r
-*\r
-*/\r
-public final class BindingBrokerImplModule extends\r
- org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingBrokerImplModule {\r
-\r
- private BundleContext bundleContext;\r
-\r
- public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {\r
- super(identifier, dependencyResolver);\r
- }\r
-\r
- public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,\r
- final BindingBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {\r
- super(identifier, dependencyResolver, oldModule, oldInstance);\r
- }\r
-\r
- @Override\r
- public void validate() {\r
- super.validate();\r
- }\r
-\r
- @Override\r
- public java.lang.AutoCloseable createInstance() {\r
-\r
- RootBindingAwareBroker broker;\r
- if (DomForwardingUtils.isDomForwardedBroker(getDataBrokerDependency())) {\r
- broker = createForwardedBroker();\r
- } else {\r
- broker = createStandaloneBroker();\r
- }\r
- broker.start();\r
- return broker;\r
- }\r
-\r
- private RootBindingAwareBroker createStandaloneBroker() {\r
- RootBindingAwareBroker broker = new RootBindingAwareBroker(getIdentifier().getInstanceName());\r
-\r
- broker.setLegacyDataBroker(getDataBrokerDependency());\r
- broker.setNotificationBroker(getNotificationServiceDependency());\r
- broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));\r
- // FIXME: Also set Async Data Broker\r
- return broker;\r
- }\r
-\r
- private RootBindingAwareBroker createForwardedBroker() {\r
- DomForwardedBindingBrokerImpl broker = new DomForwardedBindingBrokerImpl(getIdentifier().getInstanceName());\r
-\r
- broker.setLegacyDataBroker(getDataBrokerDependency());\r
- broker.setNotificationBroker(getNotificationServiceDependency());\r
- broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));\r
-\r
- broker.getMountManager().setDataCommitExecutor(SingletonHolder.getDefaultCommitExecutor());\r
- broker.getMountManager().setNotificationExecutor(SingletonHolder.getDefaultNotificationExecutor());\r
-\r
- // FIXME: Also set Async Data Broker\r
- DomForwardingUtils.reuseForwardingFrom(broker, broker.getDataBroker());\r
- broker.startForwarding();\r
- return broker;\r
- }\r
-\r
- public BundleContext getBundleContext() {\r
- return bundleContext;\r
- }\r
-\r
- public void setBundleContext(final BundleContext bundleContext) {\r
- this.bundleContext = bundleContext;\r
- }\r
-}\r
+/*
+ * 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
+ */
+/**
+ * Generated file
+
+ * Generated from: yang module name: opendaylight-sal-binding-broker-impl yang module local name: binding-broker-impl
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Wed Nov 20 17:33:01 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.md.sal.binding.impl;
+
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.controller.sal.binding.impl.RootBindingAwareBroker;
+import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
+import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBindingBrokerImpl;
+import org.opendaylight.controller.sal.binding.impl.forward.DomForwardingUtils;
+
+/**
+*
+*/
+public final class BindingBrokerImplModule extends
+ org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingBrokerImplModule {
+
+ public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final BindingBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void validate() {
+ super.validate();
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+
+ RootBindingAwareBroker broker;
+ if (DomForwardingUtils.isDomForwardedBroker(getDataBrokerDependency())) {
+ broker = createForwardedBroker();
+ } else {
+ broker = createStandaloneBroker();
+ }
+ broker.start();
+ return broker;
+ }
+
+ private RootBindingAwareBroker createStandaloneBroker() {
+ RootBindingAwareBroker broker = new RootBindingAwareBroker(getIdentifier().getInstanceName());
+
+ broker.setLegacyDataBroker(getDataBrokerDependency());
+ broker.setNotificationBroker(getNotificationServiceDependency());
+ broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));
+ broker.setDataBroker(getRootDataBrokerDependency());
+ return broker;
+ }
+
+ private RootBindingAwareBroker createForwardedBroker() {
+ DomForwardedBindingBrokerImpl broker = new DomForwardedBindingBrokerImpl(getIdentifier().getInstanceName());
+
+ broker.setLegacyDataBroker(getDataBrokerDependency());
+ broker.setNotificationBroker(getNotificationServiceDependency());
+ broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));
+
+ broker.getMountManager().setDataCommitExecutor(SingletonHolder.getDefaultCommitExecutor());
+ broker.getMountManager().setNotificationExecutor(SingletonHolder.getDefaultNotificationExecutor());
+
+ broker.setDataBroker(getRootDataBrokerDependency());
+ DomForwardingUtils.reuseForwardingFrom(broker, broker.getDataBroker());
+ broker.startForwarding();
+ return broker;
+ }
+}
*/
package org.opendaylight.controller.config.yang.md.sal.binding.impl;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
/**
*
*/
public class BindingBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingBrokerImplModuleFactory {
-
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
- BindingBrokerImplModule module = (BindingBrokerImplModule) super.createModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver,
- DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception {
- BindingBrokerImplModule module = (BindingBrokerImplModule) super.createModule(instanceName, dependencyResolver, old, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
}
* 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.md.sal.binding.impl;\r
-\r
+package org.opendaylight.controller.config.yang.md.sal.binding.impl;
+
import java.util.concurrent.ExecutorService;
import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedDataBrokerImpl;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-\r
-/**\r
-*\r
-*/\r
-public final class DataBrokerImplModule extends\r
- org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModule {\r
-\r
- private BundleContext bundleContext;\r
-\r
- public DataBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {\r
- super(identifier, dependencyResolver);\r
- }\r
-\r
- public DataBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,\r
- DataBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) {\r
- super(identifier, dependencyResolver, oldModule, oldInstance);\r
- }\r
-\r
- @Override\r
- public void validate() {\r
- super.validate();\r
- }\r
-\r
- @Override\r
- public java.lang.AutoCloseable createInstance() {\r
- RootDataBrokerImpl dataBindingBroker;\r
-\r
-\r
- ExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor();\r
- BindingIndependentMappingService potentialMapping = resolveMappingServiceDependency();\r
- if (getDomBrokerDependency() != null && potentialMapping != null) {\r
-\r
- dataBindingBroker = createDomConnectedBroker(listeningExecutor,potentialMapping);\r
- } else {\r
- dataBindingBroker = createStandAloneBroker(listeningExecutor);\r
- }\r
- dataBindingBroker.registerRuntimeBean(getRootRuntimeBeanRegistratorWrapper());\r
- dataBindingBroker.setNotificationExecutor(SingletonHolder.getDefaultChangeEventExecutor());\r
- return dataBindingBroker;\r
- }\r
- private BindingIndependentMappingService resolveMappingServiceDependency() {\r
- if(getMappingService() != null) {\r
- return getMappingServiceDependency();\r
- }\r
-\r
- ServiceReference<BindingIndependentMappingService> potentialMappingService = bundleContext.getServiceReference(BindingIndependentMappingService.class);\r
- if(potentialMappingService != null) {\r
- return bundleContext.getService(potentialMappingService);\r
- }\r
- return null;\r
- }\r
-\r
- private RootDataBrokerImpl createStandAloneBroker(ExecutorService listeningExecutor) {\r
- RootDataBrokerImpl broker = new RootDataBrokerImpl();\r
- broker.setExecutor(listeningExecutor);\r
- return broker;\r
- }\r
-\r
- private RootDataBrokerImpl createDomConnectedBroker(ExecutorService listeningExecutor, BindingIndependentMappingService mappingService) {\r
- DomForwardedDataBrokerImpl forwardedBroker = new DomForwardedDataBrokerImpl();\r
- forwardedBroker.setExecutor(listeningExecutor);\r
- BindingIndependentConnector connector = BindingDomConnectorDeployer.createConnector(mappingService);\r
- getDomBrokerDependency().registerProvider(forwardedBroker, getBundleContext());\r
- ProviderSession domContext = forwardedBroker.getDomProviderContext();\r
- forwardedBroker.setConnector(connector);\r
- forwardedBroker.setDomProviderContext(domContext);\r
- forwardedBroker.startForwarding();\r
- return forwardedBroker;\r
- }\r
-\r
- public BundleContext getBundleContext() {\r
- return bundleContext;\r
- }\r
-\r
- public void setBundleContext(BundleContext bundleContext2) {\r
- this.bundleContext = bundleContext2;\r
- }\r
-}\r
+
+/**
+*
+*/
+public final class DataBrokerImplModule extends
+ org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModule {
+
+ public DataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final DataBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void validate() {
+ super.validate();
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ RootDataBrokerImpl dataBindingBroker;
+
+
+ ExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor();
+ BindingIndependentMappingService potentialMapping = getMappingServiceDependency();
+ if (getDomBrokerDependency() != null && potentialMapping != null) {
+
+ dataBindingBroker = createDomConnectedBroker(listeningExecutor,potentialMapping);
+ } else {
+ dataBindingBroker = createStandAloneBroker(listeningExecutor);
+ }
+ dataBindingBroker.registerRuntimeBean(getRootRuntimeBeanRegistratorWrapper());
+ dataBindingBroker.setNotificationExecutor(SingletonHolder.getDefaultChangeEventExecutor());
+ return dataBindingBroker;
+ }
+
+
+ private RootDataBrokerImpl createStandAloneBroker(final ExecutorService listeningExecutor) {
+ RootDataBrokerImpl broker = new RootDataBrokerImpl();
+ broker.setExecutor(listeningExecutor);
+ return broker;
+ }
+
+ private RootDataBrokerImpl createDomConnectedBroker(final ExecutorService listeningExecutor, final BindingIndependentMappingService mappingService) {
+ DomForwardedDataBrokerImpl forwardedBroker = new DomForwardedDataBrokerImpl();
+ forwardedBroker.setExecutor(listeningExecutor);
+ BindingIndependentConnector connector = BindingDomConnectorDeployer.createConnector(mappingService);
+ getDomBrokerDependency().registerProvider(forwardedBroker, null);
+ ProviderSession domContext = forwardedBroker.getDomProviderContext();
+ forwardedBroker.setConnector(connector);
+ forwardedBroker.setDomProviderContext(domContext);
+ forwardedBroker.startForwarding();
+ return forwardedBroker;
+ }
+
+}
*/
package org.opendaylight.controller.config.yang.md.sal.binding.impl;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
/**
*
public class DataBrokerImplModuleFactory extends
org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModuleFactory {
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
- DataBrokerImplModule module = (DataBrokerImplModule) super.createModule(instanceName, dependencyResolver,
- bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver,
- DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception {
- DataBrokerImplModule module = (DataBrokerImplModule) super.createModule(instanceName, dependencyResolver, old,
- bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
}
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
-import org.osgi.framework.BundleContext;
import com.google.common.util.concurrent.ListeningExecutorService;
org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractForwardedCompatibleDataBrokerImplModule
implements Provider {
- private BundleContext bundleContext;
-
public ForwardedCompatibleDataBrokerImplModule(
final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
BindingIndependentMappingService mappingService = getBindingMappingServiceDependency();
Broker domBroker = getDomAsyncBrokerDependency();
- ProviderSession session = domBroker.registerProvider(this, getBundleContext());
+ ProviderSession session = domBroker.registerProvider(this, null);
DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
SchemaService schemaService = session.getService(SchemaService.class);
ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker,
return dataBroker;
}
- public BundleContext getBundleContext() {
- return bundleContext;
- }
-
- public void setBundleContext(final BundleContext bundleContext2) {
- this.bundleContext = bundleContext2;
- }
-
@Override
public void onSessionInitiated(final ProviderSession session) {
*/
package org.opendaylight.controller.config.yang.md.sal.binding.impl;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
/**
public class ForwardedCompatibleDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractForwardedCompatibleDataBrokerImplModuleFactory
{
-
- @Override
- public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
- ForwardedCompatibleDataBrokerImplModule module = (ForwardedCompatibleDataBrokerImplModule) super.createModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
- final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
- ForwardedCompatibleDataBrokerImplModule module = (ForwardedCompatibleDataBrokerImplModule) super.createModule(instanceName, dependencyResolver, old, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
}
*/
package org.opendaylight.controller.md.sal.binding.impl;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, DomForwardedBroker,
SchemaContextListener, AutoCloseable {
protected Map<InstanceIdentifier<?>, DataObject> toBinding(
final Map<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
Map<InstanceIdentifier<?>, DataObject> newMap = new HashMap<>();
- for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized
- .entrySet()) {
+
+ for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : sortedEntries(normalized)) {
try {
Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> potential = getCodec().toBinding(
entry);
return newMap;
}
+ private static <T> Iterable<Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier,T>> sortedEntries(final Map<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T> map) {
+ ArrayList<Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T>> entries = new ArrayList<>(map.entrySet());
+ Collections.sort(entries, new Comparator<Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T>>() {
+
+ @Override
+ public int compare(final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T> left,
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T> right) {
+ int leftSize = Iterables.size(left.getKey().getPathArguments());
+ int rightSize = Iterables.size(right.getKey().getPathArguments());
+ return Integer.compare(leftSize, rightSize);
+ }
+ });
+ return entries;
+ }
+
protected Set<InstanceIdentifier<?>> toBinding(
final Set<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> normalized) {
Set<InstanceIdentifier<?>> hashSet = new HashSet<>();
*/
package org.opendaylight.controller.md.sal.binding.impl;
-import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Nullable;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
import com.google.common.base.Optional;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-public class AbstractForwardedTransaction<T extends AsyncTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>>
- implements Delegator<T> {
- private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedTransaction.class);
+abstract class AbstractForwardedTransaction<T extends AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>>>
+ implements Delegator<T>, Identifiable<Object> {
+
private final T delegate;
- private final static CacheBuilder<Object, Object> CACHE_BUILDER = CacheBuilder.newBuilder()
- .expireAfterWrite(10, TimeUnit.MILLISECONDS).maximumSize(100);
private final BindingToNormalizedNodeCodec codec;
- private final EnumMap<LogicalDatastoreType, Cache<InstanceIdentifier<?>, DataObject>> cacheMap;
- protected AbstractForwardedTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
- super();
- this.delegate = delegate;
- this.codec = codec;
+ public AbstractForwardedTransaction(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+ this.delegate = Preconditions.checkNotNull(delegateTx, "Delegate must not be null");
+ this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
+ }
- this.cacheMap = new EnumMap<>(LogicalDatastoreType.class);
- cacheMap.put(LogicalDatastoreType.OPERATIONAL, CACHE_BUILDER.<InstanceIdentifier<?>, DataObject> build());
- cacheMap.put(LogicalDatastoreType.CONFIGURATION, CACHE_BUILDER.<InstanceIdentifier<?>, DataObject> build());
+ @Override
+ public final Object getIdentifier() {
+ return delegate.getIdentifier();
}
@Override
- public T getDelegate() {
+ public final T getDelegate() {
return delegate;
}
- protected final BindingToNormalizedNodeCodec getCodec() {
- return codec;
- }
-
- protected ListenableFuture<Optional<DataObject>> transformFuture(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final ListenableFuture<Optional<NormalizedNode<?, ?>>> future) {
- return Futures.transform(future, new Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>>() {
- @Nullable
- @Override
- public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
- if (normalizedNode.isPresent()) {
- final DataObject dataObject;
- try {
- dataObject = codec.toBinding(path, normalizedNode.get());
- } catch (DeserializationException e) {
- LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e);
- throw new IllegalStateException("Failed to create dataobject", e);
- }
-
- if (dataObject != null) {
- updateCache(store, path, dataObject);
- return Optional.of(dataObject);
- }
- }
- return Optional.absent();
- }
- });
- }
-
- protected void doPut(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final DataObject data) {
- invalidateCache(store, path);
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
- .toNormalizedNode(path, data);
- writeTransaction.put(store, normalized.getKey(), normalized.getValue());
- }
-
- protected void doPutWithEnsureParents(final DOMDataReadWriteTransaction writeTransaction,
- final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- invalidateCache(store, path);
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
- .toNormalizedNode(path, data);
-
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
- ensureParentsByMerge(writeTransaction, store, normalizedPath, path);
- LOG.debug("Tx: {} : Putting data {}", getDelegate().getIdentifier(), normalizedPath);
- writeTransaction.put(store, normalizedPath, normalized.getValue());
- }
-
- protected void doMergeWithEnsureParents(final DOMDataReadWriteTransaction writeTransaction,
- final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- invalidateCache(store, path);
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
- .toNormalizedNode(path, data);
-
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
- ensureParentsByMerge(writeTransaction, store, normalizedPath, path);
- LOG.debug("Tx: {} : Merge data {}",getDelegate().getIdentifier(),normalizedPath);
- writeTransaction.merge(store, normalizedPath, normalized.getValue());
+ @SuppressWarnings("unchecked")
+ protected final <S extends AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>>> S getDelegateChecked(final Class<S> txType) {
+ Preconditions.checkState(txType.isInstance(delegate));
+ return (S) delegate;
}
- private void ensureParentsByMerge(final DOMDataReadWriteTransaction writeTransaction,
- final LogicalDatastoreType store,
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath,
- final InstanceIdentifier<?> path) {
- List<PathArgument> currentArguments = new ArrayList<>();
- DataNormalizationOperation<?> currentOp = codec.getDataNormalizer().getRootOperation();
- Iterator<PathArgument> iterator = normalizedPath.getPath().iterator();
- while (iterator.hasNext()) {
- PathArgument currentArg = iterator.next();
- try {
- currentOp = currentOp.getChild(currentArg);
- } catch (DataNormalizationException e) {
- throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
- }
- currentArguments.add(currentArg);
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier currentPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
- currentArguments);
-
- final Optional<NormalizedNode<?, ?>> d;
- try {
- d = writeTransaction.read(store, currentPath).get();
- } catch (InterruptedException | ExecutionException e) {
- LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
- throw new IllegalStateException("Failed to read pre-existing data", e);
- }
-
- if (!d.isPresent() && iterator.hasNext()) {
- writeTransaction.merge(store, currentPath, currentOp.createDefault(currentArg));
- }
- }
- }
-
- protected void doMerge(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final DataObject data) {
- invalidateCache(store, path);
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = codec
- .toNormalizedNode(path, data);
- writeTransaction.merge(store, normalized.getKey(), normalized.getValue());
- }
-
- protected void doDelete(final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType store,
- final InstanceIdentifier<?> path) {
- invalidateCache(store, path);
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = codec.toNormalized(path);
- writeTransaction.delete(store, normalized);
- }
-
- protected ListenableFuture<RpcResult<TransactionStatus>> doCommit(final DOMDataWriteTransaction writeTransaction) {
- return writeTransaction.commit();
- }
-
- protected boolean doCancel(final DOMDataWriteTransaction writeTransaction) {
- return writeTransaction.cancel();
- }
-
- protected ListenableFuture<Optional<DataObject>> doRead(final DOMDataReadTransaction readTransaction,
- final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
- final DataObject dataObject = getFromCache(store, path);
- if (dataObject == null) {
- final ListenableFuture<Optional<NormalizedNode<?, ?>>> future = readTransaction.read(store,
- codec.toNormalized(path));
- return transformFuture(store, path, future);
- } else {
- return Futures.immediateFuture(Optional.of(dataObject));
- }
- }
-
- private DataObject getFromCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
- Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
- if (cache != null) {
- return cache.getIfPresent(path);
- }
- return null;
- }
-
- private void updateCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
- final DataObject dataObject) {
- // Check if cache exists. If not create one.
- Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
- if (cache == null) {
- cache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES).build();
-
- }
-
- cache.put(path, dataObject);
+ protected final BindingToNormalizedNodeCodec getCodec() {
+ return codec;
}
- private void invalidateCache(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
- // FIXME: Optimization: invalidate only parents and children of path
- Cache<InstanceIdentifier<?>, DataObject> cache = cacheMap.get(store);
- cache.invalidateAll();
- LOG.trace("Cache invalidated");
+ protected final ListenableFuture<Optional<DataObject>> doRead(final DOMDataReadTransaction readTx,
+ final LogicalDatastoreType store, final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> path) {
+ return Futures.transform(readTx.read(store, codec.toNormalized(path)), codec.deserializeFunction(path));
}
-
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class AbstractReadWriteTransaction extends AbstractWriteTransaction<DOMDataReadWriteTransaction> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractReadWriteTransaction.class);
+
+ public AbstractReadWriteTransaction(final DOMDataReadWriteTransaction delegate, final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ protected final void doPutWithEnsureParents(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
+ .toNormalizedNode(path, data);
+
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
+ ensureParentsByMerge(store, normalizedPath, path);
+ LOG.debug("Tx: {} : Putting data {}", getDelegate().getIdentifier(), normalizedPath);
+ doPut(store, path, data);
+ }
+
+ protected final void doMergeWithEnsureParents(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
+ .toNormalizedNode(path, data);
+
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
+ ensureParentsByMerge(store, normalizedPath, path);
+ LOG.debug("Tx: {} : Merge data {}", getDelegate().getIdentifier(), normalizedPath);
+ doMerge(store, path, data);
+ }
+
+ private final void ensureParentsByMerge(final LogicalDatastoreType store,
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath,
+ final InstanceIdentifier<?> path) {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ while (iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+ }
+ currentArguments.add(currentArg);
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(
+ currentArguments);
+
+ final Optional<NormalizedNode<?, ?>> d;
+ try {
+ d = getDelegate().read(store, currentPath).get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
+ throw new IllegalStateException("Failed to read pre-existing data", e);
+ }
+
+ if (!d.isPresent() && iterator.hasNext()) {
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Abstract Base Transaction for transactions which are backed by
+ * {@link DOMDataWriteTransaction}
+ */
+public class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
+ AbstractForwardedTransaction<T> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractWriteTransaction.class);
+
+ protected AbstractWriteTransaction(final T delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ protected final void doPut(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final DataObject data) {
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
+ .toNormalizedNode(path, data);
+ getDelegate().put(store, normalized.getKey(), normalized.getValue());
+ }
+
+ protected final void doMerge(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final DataObject data) {
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
+ .toNormalizedNode(path, data);
+ getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ }
+
+ protected final void doDelete(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized = getCodec().toNormalized(path);
+ getDelegate().delete(store, normalized);
+ }
+
+ protected final ListenableFuture<RpcResult<TransactionStatus>> doCommit() {
+ return getDelegate().commit();
+ }
+
+ protected final boolean doCancel() {
+ return getDelegate().cancel();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+class BindingDataReadTransactionImpl extends AbstractForwardedTransaction<DOMDataReadOnlyTransaction> implements
+ ReadOnlyTransaction {
+
+ protected BindingDataReadTransactionImpl(final DOMDataReadOnlyTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ return doRead(getDelegate(),store, path);
+ }
+
+ @Override
+ public void close() {
+ getDelegate().close();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+class BindingDataReadWriteTransactionImpl extends
+ BindingDataWriteTransactionImpl<DOMDataReadWriteTransaction> implements ReadWriteTransaction {
+
+ protected BindingDataReadWriteTransactionImpl(final DOMDataReadWriteTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ return doRead(getDelegate(), store, path);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+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.util.concurrent.ListenableFuture;
+
+class BindingDataWriteTransactionImpl<T extends DOMDataWriteTransaction> extends
+ AbstractWriteTransaction<T> implements WriteTransaction {
+
+ protected BindingDataWriteTransactionImpl(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+ super(delegateTx, codec);
+ }
+
+
+
+ @Override
+ public void put(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ doPut(store, path, data);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
+ doMerge(store, path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ doDelete( store, path);
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return doCommit();
+ }
+
+ @Override
+ public boolean cancel() {
+ return doCancel();
+ }
+}
\ No newline at end of file
import java.util.List;
import java.util.Map.Entry;
+import javax.annotation.Nullable;
+
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Function;
import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
public Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
final InstanceIdentifier<? extends DataObject> bindingPath, final DataObject bindingObject) {
- return toNormalizedNode(toEntry(bindingPath, bindingObject));
+ return toNormalizedNode(toBindingEntry(bindingPath, bindingObject));
}
.toNormalized(legacyEntry);
LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding,
legacyEntry, normalizedEntry);
- if (Augmentation.class.isAssignableFrom(binding.getKey().getTargetType())) {
+ if (isAugmentation(binding.getKey().getTargetType())) {
for (DataContainerChild<? extends PathArgument, ?> child : ((DataContainerNode<?>) normalizedEntry
.getValue()).getValue()) {
if (child instanceof AugmentationNode) {
ImmutableList<PathArgument> childArgs = ImmutableList.<PathArgument> builder()
- .addAll(normalizedEntry.getKey().getPath()).add(child.getIdentifier()).build();
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
+ .addAll(normalizedEntry.getKey().getPathArguments()).add(child.getIdentifier()).build();
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(
childArgs);
- return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
- childPath, child);
+ return toDOMEntry(childPath, child);
}
}
*/
public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DeserializationException {
+ throws DeserializationException {
- PathArgument lastArgument = Iterables.getLast(normalized.getPath());
+ PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments());
// Used instance-identifier codec do not support serialization of last
// path
// argument if it is AugmentationIdentifier (behaviour expected by old
private Optional<InstanceIdentifier<? extends DataObject>> toBindingAugmented(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DeserializationException {
+ throws DeserializationException {
Optional<InstanceIdentifier<? extends DataObject>> potential = toBindingImpl(normalized);
// Shorthand check, if codec already supports deserialization
// of AugmentationIdentifier we will return
}
int normalizedCount = getAugmentationCount(normalized);
- AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPath());
+ AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments());
// Here we employ small trick - Binding-aware Codec injects an pointer
// to augmentation class
// path.
LOG.trace("Looking for candidates to match {}", normalized);
for (QName child : lastArgument.getPossibleChildNames()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
- ImmutableList.<PathArgument> builder().addAll(normalized.getPath()).add(new NodeIdentifier(child))
- .build());
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier childPath = normalized.node(child);
try {
if (isNotRepresentable(childPath)) {
LOG.trace("Path {} is not BI-representable, skipping it", childPath);
private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DeserializationException {
+ throws DeserializationException {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
try {
private DataNormalizationOperation<?> findNormalizationOperation(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DataNormalizationException {
+ throws DataNormalizationException {
DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
- for (PathArgument arg : normalized.getPath()) {
+ for (PathArgument arg : normalized.getPathArguments()) {
current = current.getChild(arg);
}
return current;
}
- private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toEntry(
+ private static final Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject> toBindingEntry(
final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject> key,
final DataObject value) {
return new SimpleEntry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>(
key, value);
}
+ private static final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> toDOMEntry(
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key,
+ final NormalizedNode<?, ?> value) {
+ return new SimpleEntry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>(
+ key, value);
+ }
+
public DataObject toBinding(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> normalizedNode)
throws DeserializationException {
CompositeNode legacy = null;
public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
- throws DeserializationException {
+ throws DeserializationException {
Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
if (potentialPath.isPresent()) {
InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
if (bindingData == null) {
LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath);
}
- return Optional.of(toEntry(bindingPath, bindingData));
+ return Optional.of(toBindingEntry(bindingPath, bindingData));
} else {
return Optional.absent();
}
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized) {
int position = 0;
int foundPosition = -1;
- for (PathArgument arg : normalized.getPath()) {
+ for (PathArgument arg : normalized.getPathArguments()) {
position++;
if (arg instanceof AugmentationIdentifier) {
foundPosition = position;
}
}
if (foundPosition > 0) {
- return new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(normalized.getPath().subList(0,
- foundPosition));
+ Iterable<PathArgument> shortened = Iterables.limit(normalized.getPathArguments(), foundPosition);
+ return org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(shortened);
}
return null;
}
try {
return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
new Supplier<Class>() {
- @Override
- public Class get() {
- Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
- .getGenericReturnType());
- if (listResult instanceof Class
- && DataObject.class.isAssignableFrom((Class) listResult)) {
- return (Class<?>) listResult;
- }
- return null;
- }
-
- });
+ @Override
+ public Class get() {
+ Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
+ .getGenericReturnType());
+ if (listResult instanceof Class
+ && DataObject.class.isAssignableFrom((Class) listResult)) {
+ return (Class<?>) listResult;
+ }
+ return null;
+ }
+
+ });
} catch (Exception e) {
LOG.debug("Could not get YANG modeled entity for {}", method, e);
return null;
}
private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier processed) {
- return Iterables.getLast(processed.getPath()) instanceof AugmentationIdentifier;
+ return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier;
}
private static int getAugmentationCount(final InstanceIdentifier<?> potential) {
private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier potential) {
int count = 0;
- for(PathArgument arg : potential.getPath()) {
+ for(PathArgument arg : potential.getPathArguments()) {
if(arg instanceof AugmentationIdentifier) {
count++;
}
}
return count;
}
+
+ public Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> deserializeFunction(final InstanceIdentifier<?> path) {
+ return new DeserializeFunction(this, path);
+ }
+
+ private static class DeserializeFunction implements Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> {
+
+ private final BindingToNormalizedNodeCodec codec;
+ private final InstanceIdentifier<?> path;
+
+ public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier<?> path) {
+ super();
+ this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
+ this.path = Preconditions.checkNotNull(path, "Path must not be null");
+ }
+
+ @Nullable
+ @Override
+ public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
+ if (normalizedNode.isPresent()) {
+ final DataObject dataObject;
+ try {
+ dataObject = codec.toBinding(path, normalizedNode.get());
+ } catch (DeserializationException e) {
+ LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e);
+ throw new IllegalStateException("Failed to create dataobject", e);
+ }
+
+ if (dataObject != null) {
+ return Optional.of(dataObject);
+ }
+ }
+ return Optional.absent();
+ }
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.yangtools.concepts.Delegator;
+
+import com.google.common.base.Preconditions;
+
+class BindingTranslatedTransactionChain implements BindingTransactionChain, Delegator<DOMTransactionChain> {
+
+ private final DOMTransactionChain delegate;
+
+ @GuardedBy("this")
+ private final Map<AsyncTransaction<?, ?>, AsyncTransaction<?, ?>> delegateTxToBindingTx = new WeakHashMap<>();
+ private final BindingToNormalizedNodeCodec codec;
+
+ public BindingTranslatedTransactionChain(final DOMDataBroker chainFactory,
+ final BindingToNormalizedNodeCodec codec, final TransactionChainListener listener) {
+ Preconditions.checkNotNull(chainFactory, "DOM Transaction chain factory must not be null");
+ this.delegate = chainFactory.createTransactionChain(new ListenerInvoker(listener));
+ this.codec = codec;
+ }
+
+ @Override
+ public DOMTransactionChain getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public ReadOnlyTransaction newReadOnlyTransaction() {
+ DOMDataReadOnlyTransaction delegateTx = delegate.newReadOnlyTransaction();
+ ReadOnlyTransaction bindingTx = new BindingDataReadTransactionImpl(delegateTx, codec);
+ putDelegateToBinding(delegateTx, bindingTx);
+ return bindingTx;
+ }
+
+ @Override
+ public ReadWriteTransaction newReadWriteTransaction() {
+ DOMDataReadWriteTransaction delegateTx = delegate.newReadWriteTransaction();
+ ReadWriteTransaction bindingTx = new BindingDataReadWriteTransactionImpl(delegateTx, codec);
+ putDelegateToBinding(delegateTx, bindingTx);
+ return bindingTx;
+ }
+
+ @Override
+ public WriteTransaction newWriteOnlyTransaction() {
+ DOMDataWriteTransaction delegateTx = delegate.newWriteOnlyTransaction();
+ WriteTransaction bindingTx = new BindingDataWriteTransactionImpl<>(delegateTx, codec);
+ putDelegateToBinding(delegateTx, bindingTx);
+ return bindingTx;
+ }
+
+ @Override
+ public void close() {
+ delegate.close();
+ }
+
+ private synchronized void putDelegateToBinding(final AsyncTransaction<?, ?> domTx,
+ final AsyncTransaction<?, ?> bindingTx) {
+ final Object previous = delegateTxToBindingTx.put(domTx, bindingTx);
+ Preconditions.checkState(previous == null, "DOM Transaction %s has already associated binding transation %s",domTx,previous);
+ }
+
+ private synchronized AsyncTransaction<?, ?> getBindingTransaction(final AsyncTransaction<?, ?> transaction) {
+ return delegateTxToBindingTx.get(transaction);
+ }
+
+ private final class ListenerInvoker implements TransactionChainListener {
+
+ private final TransactionChainListener listener;
+
+ public ListenerInvoker(final TransactionChainListener listener) {
+ this.listener = Preconditions.checkNotNull(listener, "Listener must not be null.");
+ }
+
+ @Override
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
+ final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+ Preconditions.checkState(delegate.equals(chain),
+ "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+ AsyncTransaction<?, ?> bindingTx = getBindingTransaction(transaction);
+ listener.onTransactionChainFailed(chain, bindingTx, cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+ Preconditions.checkState(delegate.equals(chain),
+ "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+ listener.onTransactionChainSuccessful(BindingTranslatedTransactionChain.this);
+ }
+ }
+
+}
}
private class ForwardedBackwardsCompatibleTransacion extends
- AbstractForwardedTransaction<DOMDataReadWriteTransaction> implements DataModificationTransaction {
+ AbstractReadWriteTransaction implements DataModificationTransaction {
private final ListenerRegistry<DataTransactionListener> listeners = ListenerRegistry.create();
private final Map<InstanceIdentifier<? extends DataObject>, DataObject> updated = new HashMap<>();
public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
boolean previouslyRemoved = posponedRemovedOperational.remove(path);
if(previouslyRemoved) {
- doPutWithEnsureParents(getDelegate(), LogicalDatastoreType.OPERATIONAL, path, data);
+ doPutWithEnsureParents(LogicalDatastoreType.OPERATIONAL, path, data);
} else {
- doMergeWithEnsureParents(getDelegate(), LogicalDatastoreType.OPERATIONAL, path, data);
+ doMergeWithEnsureParents(LogicalDatastoreType.OPERATIONAL, path, data);
}
}
}
updated.put(path, data);
if(previouslyRemoved) {
- doPutWithEnsureParents(getDelegate(), LogicalDatastoreType.CONFIGURATION, path, data);
+ doPutWithEnsureParents(LogicalDatastoreType.CONFIGURATION, path, data);
} else {
- doMergeWithEnsureParents(getDelegate(), LogicalDatastoreType.CONFIGURATION, path, data);
+ doMergeWithEnsureParents(LogicalDatastoreType.CONFIGURATION, path, data);
}
}
}
}
- @Override
- public Object getIdentifier() {
- return getDelegate().getIdentifier();
- }
-
private void changeStatus(final TransactionStatus status) {
LOG.trace("Transaction {} changed status to {}", getIdentifier(), status);
this.status = status;
public ListenableFuture<RpcResult<TransactionStatus>> commit() {
for(InstanceIdentifier<? extends DataObject> path : posponedRemovedConfiguration) {
- doDelete(getDelegate(), LogicalDatastoreType.CONFIGURATION, path);
+ doDelete(LogicalDatastoreType.CONFIGURATION, path);
}
for(InstanceIdentifier<? extends DataObject> path : posponedRemovedOperational) {
- doDelete(getDelegate(), LogicalDatastoreType.OPERATIONAL, path);
+ doDelete(LogicalDatastoreType.OPERATIONAL, path);
}
changeStatus(TransactionStatus.SUBMITED);
package org.opendaylight.controller.md.sal.binding.impl;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
-
/**
* The DataBrokerImpl simply defers to the DOMDataBroker for all its operations.
* All transactions and listener registrations are wrapped by the DataBrokerImpl
* Besides this the DataBrokerImpl and it's collaborators also cache data that
* is already transformed from the binding independent to binding aware format
*
- * TODO : All references in this class to CompositeNode should be switched to
- * NormalizedNode once the MappingService is updated
- *
+
*/
public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements DataBroker {
@Override
public WriteTransaction newWriteOnlyTransaction() {
- return new BindingDataWriteTransactionImpl<DOMDataWriteTransaction>(getDelegate().newWriteOnlyTransaction(),getCodec());
- }
-
- private abstract class AbstractBindingTransaction<T extends AsyncTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>>>
- extends AbstractForwardedTransaction<T> implements AsyncTransaction<InstanceIdentifier<?>, DataObject> {
-
- protected AbstractBindingTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
- super(delegate, codec);
- }
-
- @Override
- public Object getIdentifier() {
- return getDelegate().getIdentifier();
- }
-
- }
-
-
- private class BindingDataReadTransactionImpl extends AbstractBindingTransaction<DOMDataReadOnlyTransaction> implements
- ReadOnlyTransaction {
-
- protected BindingDataReadTransactionImpl(final DOMDataReadOnlyTransaction delegate,
- final BindingToNormalizedNodeCodec codec) {
- super(delegate, codec);
- }
-
- @Override
- public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path) {
- return doRead(getDelegate(), store, path);
- }
-
- @Override
- public void close() {
- getDelegate().close();
- }
+ return new BindingDataWriteTransactionImpl<>(getDelegate().newWriteOnlyTransaction(),getCodec());
}
- private class BindingDataWriteTransactionImpl<T extends DOMDataWriteTransaction> extends
- AbstractBindingTransaction<T> implements WriteTransaction {
-
- protected BindingDataWriteTransactionImpl(final T delegate, final BindingToNormalizedNodeCodec codec) {
- super(delegate, codec);
-
- }
-
- @Override
- public boolean cancel() {
- return doCancel(getDelegate());
- }
-
- @Override
- public void put(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- doPut(getDelegate(), store, path, data);
- }
-
- @Override
- public void merge(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- doMerge(getDelegate(), store, path, data);
- }
-
- @Override
- public void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
- doDelete(getDelegate(), store, path);
- }
-
- @Override
- public ListenableFuture<RpcResult<TransactionStatus>> commit() {
- return doCommit(getDelegate());
- }
- }
-
- private class BindingDataReadWriteTransactionImpl extends
- BindingDataWriteTransactionImpl<DOMDataReadWriteTransaction> implements ReadWriteTransaction {
-
- protected BindingDataReadWriteTransactionImpl(final DOMDataReadWriteTransaction delegate,
- final BindingToNormalizedNodeCodec codec) {
- super(delegate, codec);
- }
-
- @Override
- public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path) {
- return doRead(getDelegate(), store, path);
- }
+ @Override
+ public BindingTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return new BindingTranslatedTransactionChain(getDelegate(), getCodec(), listener);
}
}
container data-broker {
uses config:service-ref {
refine type {
- mandatory true;
+ mandatory false;
config:required-identity sal:binding-data-broker;
}
}
}
}
}
+
+ container root-data-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity sal:binding-async-data-broker;
+ }
+ }
+ }
}
}
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.MutableClassToInstanceMap;
import com.google.common.util.concurrent.ListeningExecutorService;
public class BindingTestContext implements AutoCloseable {
newDOMDataBroker = new DOMDataBrokerImpl(newDatastores, executor);
- biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker);
+ biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker,mockSchemaService);
mockSchemaService.registerSchemaServiceListener(configStore);
mockSchemaService.registerSchemaServiceListener(operStore);
- mockSchemaService.registerSchemaServiceListener(biCompatibleBroker);
biDataLegacyBroker = biCompatibleBroker;
}
private void startDomBroker() {
checkState(executor != null);
- biBrokerImpl = new BrokerImpl();
- biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", mockSchemaService));
+
+ SchemaAwareRpcBroker router = new SchemaAwareRpcBroker("/", mockSchemaService);
+ ClassToInstanceMap<BrokerService> services = MutableClassToInstanceMap.create();
+ biBrokerImpl = new BrokerImpl(router,services);
}
</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>
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ *
+ *
+ * @param <P>
+ * @param <D>
+ * @deprecated Replaced by {@link AsyncDataChangeEvent}
+ */
+@Deprecated
public interface DataChangeEvent<P extends Path<P>,D> extends DataChange<P, D>, Immutable {
/**
import java.util.EventListener;
import org.opendaylight.yangtools.concepts.Path;
-
+/**
+ *
+ *
+ * @deprecated Replaced by {@link AsyncDataChangeEvent}
+ */
+@Deprecated
public interface DataChangeListener<P extends Path<P>, D> extends EventListener {
/**
* Note that this method may be invoked from a shared thread pool, so
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ * @deprecated Replaced by {@link AsyncDataBroker}
+ */
+@Deprecated
public interface DataChangePublisher<P extends Path<P>, D, L extends DataChangeListener<P,D>> {
ListenerRegistration<L> registerDataChangeListener(P path, L listener);
*
* @param <P> Class representing a path
* @param <D> Superclass from which all data objects are derived from.
+ * @deprecated Replaced by {@link AsyncConfigurationCommitHandler}
*/
+@Deprecated
public interface DataCommitHandler<P extends Path<P>, D> {
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.concepts.Registration;
+/**
+ *
+ *
+ * @deprecated THis was intended as Registration object, normal use of {@link org.opendaylight.yangtools.concepts.ObjectRegistration}
+ * is suffiecient, since {@link #getPath()} was implementation leak.
+ *
+ * @param <P>
+ * @param <D>
+ */
+@Deprecated
public interface DataCommitHandlerRegistration<P extends Path<P>,D> extends Registration<DataCommitHandler<P, D>>{
P getPath();
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.yang.common.RpcResult;
-
+/**
+ *
+ * @deprecated Replaced by {@link AsyncWriteTransaction}
+ */
+@Deprecated
public interface DataModification<P extends Path<P>, D> extends DataChange<P, D>, DataReader<P, D> {
/**
* Returns transaction identifier
package org.opendaylight.controller.md.sal.common.api.data;
import org.opendaylight.yangtools.concepts.Path;
-
+/**
+ *
+ * @deprecated Replaced by {@link AsyncDataTransactionFactory}
+ */
+@Deprecated
public interface DataModificationTransactionFactory<P extends Path<P> ,D> {
DataModification<P, D> beginTransaction();
import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+ */
+@Deprecated
public interface DataProvider<P extends Path<P>, D> extends DataReader<P, D> {
}
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.concepts.Registration;
-
+/**
+ *
+ *
+ * @deprecated replaced by {@link AsyncDataBroker} and {@link AsyncConfigurationCommitCoordinator}
+ */
+@Deprecated
public interface DataProvisionService<P extends Path<P> , D> {
public Registration<DataCommitHandler<P, D>> registerCommitHandler(P path, DataCommitHandler<P, D> commitHandler);
*
* @param <P> Path Type
* @param <D> Data Type
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
*/
+@Deprecated
public interface DataReader<P extends Path<P> ,D> {
/**
import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore Contract.
+ */
+@Deprecated
public interface DataStore<P extends Path<P>, D> extends //
DataReader<P, D>, //
DataModificationTransactionFactory<P, D> {
import java.util.Map;
import java.util.Set;
-
+/**
+ * Event representing change in RPC routing table.
+ *
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
public interface RouteChange<C,P> {
+ /**
+ *
+ * Returns a map of removed routes in associated routing contexts.
+ * <p>
+ * This map represents routes, which were withdrawn from broker local
+ * routing table and broker may need to forward RPC to other broker
+ * in order to process RPC request.
+ *
+ * @return Map of contexts and removed routes
+ */
Map<C,Set<P>> getRemovals();
+ /**
+ *
+ * Returns a map of announced routes in associated routing contexts.
+ *
+ * This map represents routes, which were announced by broker
+ * and are present in broker's local routing table. This routes
+ * are processed by implementations which are registered
+ * to originating broker.
+ *
+ * @return Map of contexts and announced routes
+ */
Map<C,Set<P>> getAnnouncements();
}
package org.opendaylight.controller.md.sal.common.api.routing;
import java.util.EventListener;
-
+/**
+ *
+ * Listener which is interested in receiving RouteChangeEvents
+ * for its local broker.
+ * <p>
+ * Listener is registerd via {@link RouteChangePublisher#registerRouteChangeListener(RouteChangeListener)}
+ *
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
public interface RouteChangeListener<C,P> extends EventListener {
+ /**
+ * Callback which is invoked if there is an rpc routing table change.
+ *
+ * @param change Event representing change in local RPC routing table.
+ */
void onRouteChange(RouteChange<C, P> change);
}
import org.opendaylight.yangtools.concepts.ListenerRegistration;
+/**
+ * Publishes changes in local RPC routing table to registered listener.
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
public interface RouteChangePublisher<C,P> {
<L extends RouteChangeListener<C,P>> ListenerRegistration<L> registerRouteChangeListener(L listener);
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.concepts.Registration;
+/**
+ * Base interface for a routed RPC RPC implementation registration.
+ *
+ * @param <C> the context type used for routing
+ * @param <P> the path identifier type
+ * @param <S> the RPC implementation type
+ */
public interface RoutedRegistration<C, P extends Path<P>, S> extends Registration<S> {
+ /**
+ * Registers the RPC implementation associated with this registration for the given path
+ * identifier and context.
+ *
+ * @param context the context used for routing RPCs to this implementation.
+ * @param path the path identifier for which to register.
+ */
void registerPath(C context, P path);
+
+ /**
+ * Unregisters the RPC implementation associated with this registration for the given path
+ * identifier and context.
+ *
+ * @param context the context used for routing RPCs to this implementation.
+ * @param path the path identifier for which to unregister.
+ */
void unregisterPath(C context, P path);
@Override
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
}
private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
- DataNormalizationOperation<T> {
+ DataNormalizationOperation<T> {
protected CompositeNodeNormalizationOperation(final T identifier) {
super(identifier);
}
private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
- CompositeNodeNormalizationOperation<T> {
+ CompositeNodeNormalizationOperation<T> {
private final DataNodeContainer schema;
private final Map<QName, DataNormalizationOperation<?>> byQName;
}
private static final class ListItemNormalization extends
- DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+ DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
private final List<QName> keyDefinition;
}
private static abstract class MixinNormalizationOp<T extends PathArgument> extends
- CompositeNodeNormalizationOperation<T> {
+ CompositeNodeNormalizationOperation<T> {
protected MixinNormalizationOp(final T identifier) {
super(identifier);
private static class AnyXmlNormalization extends DataNormalizationOperation<NodeIdentifier> {
- protected AnyXmlNormalization( NodeIdentifier identifier ) {
+ protected AnyXmlNormalization( final NodeIdentifier identifier ) {
super( identifier );
}
@Override
- public DataNormalizationOperation<?> getChild( PathArgument child ) throws DataNormalizationException {
+ public DataNormalizationOperation<?> getChild( final PathArgument child ) throws DataNormalizationException {
return null;
}
@Override
- public DataNormalizationOperation<?> getChild( QName child ) throws DataNormalizationException {
+ public DataNormalizationOperation<?> getChild( final QName child ) throws DataNormalizationException {
return null;
}
@Override
- public NormalizedNode<?, ?> normalize( Node<?> legacyData ) {
+ public NormalizedNode<?, ?> normalize( final Node<?> legacyData ) {
NormalizedNodeAttrBuilder<NodeIdentifier, Node<?>, AnyXmlNode> builder =
Builders.anyXmlBuilder().withNodeIdentifier(
- new NodeIdentifier( legacyData.getNodeType() ) );
+ new NodeIdentifier( legacyData.getNodeType() ) );
builder.withValue(legacyData);
return builder.build();
}
}
@Override
- public NormalizedNode<?, ?> createDefault( PathArgument currentArg ) {
+ public NormalizedNode<?, ?> createDefault( final PathArgument currentArg ) {
return null;
}
}
for (DataSchemaNode child : augmentation.getChildNodes()) {
potentialChildren.add(child.getQName());
}
- return new AugmentationIdentifier(null, potentialChildren.build());
+ return new AugmentationIdentifier(potentialChildren.build());
}
private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
+
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
ImmutableList.Builder<PathArgument> normalizedArgs = ImmutableList.builder();
DataNormalizationOperation<?> currentOp = operation;
- Iterator<PathArgument> arguments = legacy.getPath().iterator();
+ Iterator<PathArgument> arguments = legacy.getPathArguments().iterator();
try {
while (arguments.hasNext()) {
throw new IllegalArgumentException(String.format("Failed to normalize path %s", legacy), e);
}
- return new InstanceIdentifier(normalizedArgs.build());
+ return InstanceIdentifier.create(normalizedArgs.build());
}
public Map.Entry<InstanceIdentifier, NormalizedNode<?, ?>> toNormalized(
InstanceIdentifier normalizedPath = toNormalized(legacyPath);
DataNormalizationOperation<?> currentOp = operation;
- for (PathArgument arg : normalizedPath.getPath()) {
+ for (PathArgument arg : normalizedPath.getPathArguments()) {
try {
currentOp = currentOp.getChild(arg);
} catch (DataNormalizationException e) {
if (potentialOp.getIdentifier() instanceof AugmentationIdentifier) {
currentOp = potentialOp;
- ArrayList<PathArgument> reworkedArgs = new ArrayList<>(normalizedPath.getPath());
- reworkedArgs.add(potentialOp.getIdentifier());
- normalizedPath = new InstanceIdentifier(reworkedArgs);
+ normalizedPath = normalizedPath.node(potentialOp.getIdentifier());
}
}
public InstanceIdentifier toLegacy(final InstanceIdentifier normalized) throws DataNormalizationException {
ImmutableList.Builder<PathArgument> legacyArgs = ImmutableList.builder();
- PathArgument previous = null;
DataNormalizationOperation<?> currentOp = operation;
- for (PathArgument normalizedArg : normalized.getPath()) {
+ for (PathArgument normalizedArg : normalized.getPathArguments()) {
currentOp = currentOp.getChild(normalizedArg);
if(!currentOp.isMixin()) {
legacyArgs.add(normalizedArg);
}
}
- return new InstanceIdentifier(legacyArgs.build());
+ return InstanceIdentifier.create(legacyArgs.build());
}
public CompositeNode toLegacy(final InstanceIdentifier normalizedPath, final NormalizedNode<?, ?> normalizedData) {
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import java.util.AbstractMap;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
Class<?> nodeClass;
Object nodeData; // List for a container, value Object for a leaf
- NormalizedNodeData(PathArgument nodeID, Class<?> nodeClass, Object nodeData) {
+ NormalizedNodeData(final PathArgument nodeID, final Class<?> nodeClass, final Object nodeData) {
this.nodeID = nodeID;
this.nodeClass = nodeClass;
this.nodeData = nodeData;
static class LegacyNodeData {
QName nodeKey;
Object nodeData; // List for a CompositeNode, value Object for a
- // SimpeNode
+ // SimpeNode
- LegacyNodeData(QName nodeKey, Object nodeData) {
+ LegacyNodeData(final QName nodeKey, final Object nodeData) {
this.nodeKey = nodeKey;
this.nodeData = nodeData;
}
OUTER_LIST_QNAME, ID_QNAME, OUTER_LIST_ID }, OUTER_CHOICE_QNAME, TWO_QNAME);
}
- private void verifyNormalizedInstanceIdentifier(InstanceIdentifier actual, Object... expPath) {
+ private void verifyNormalizedInstanceIdentifier(final InstanceIdentifier actual, final Object... expPath) {
assertNotNull("Actual InstanceIdentifier is null", actual);
- assertEquals("InstanceIdentifier path length", expPath.length, actual.getPath().size());
+ assertEquals("InstanceIdentifier path length", expPath.length, Iterables.size(actual.getPathArguments()));
for (int i = 0; i < expPath.length; i++) {
- PathArgument actualArg = actual.getPath().get(i);
+ PathArgument actualArg = Iterables.get(actual.getPathArguments(), i);
if (expPath[i] instanceof Object[]) { // NodeIdentifierWithPredicates
Object[] exp = (Object[]) expPath[i];
assertEquals("Actual path arg " + (i + 1) + " class", NodeIdentifierWithPredicates.class,
expectCompositeNode(INNER_LIST_QNAME, expectSimpleNode(NAME_QNAME, "inner-name1"),
expectSimpleNode(VALUE_QNAME, "inner-value1")),
- expectCompositeNode(INNER_LIST_QNAME, expectSimpleNode(NAME_QNAME, "inner-name2"),
- expectSimpleNode(VALUE_QNAME, "inner-value2"))),
- expectCompositeNode(OUTER_LIST_QNAME, expectSimpleNode(ID_QNAME, outerListID2),
- expectSimpleNode(ONE_QNAME, "one")),
- expectCompositeNode(UNKEYED_LIST_QNAME, expectSimpleNode(NAME_QNAME, "unkeyed1")),
- expectCompositeNode(UNKEYED_LIST_QNAME, expectSimpleNode(NAME_QNAME, "unkeyed2"))));
+ expectCompositeNode(INNER_LIST_QNAME, expectSimpleNode(NAME_QNAME, "inner-name2"),
+ expectSimpleNode(VALUE_QNAME, "inner-value2"))),
+ expectCompositeNode(OUTER_LIST_QNAME, expectSimpleNode(ID_QNAME, outerListID2),
+ expectSimpleNode(ONE_QNAME, "one")),
+ expectCompositeNode(UNKEYED_LIST_QNAME, expectSimpleNode(NAME_QNAME, "unkeyed1")),
+ expectCompositeNode(UNKEYED_LIST_QNAME, expectSimpleNode(NAME_QNAME, "unkeyed2"))));
// Conversion of Mixin type nodes is not supported.
expectSimpleNode(AUGMENTED_LEAF_QNAME, "augmented-value"))));
}
- private boolean isOrdered(QName nodeName) {
+ private boolean isOrdered(final QName nodeName) {
return ORDERED_LEAF_LIST_QNAME.equals(nodeName) || INNER_LIST_QNAME.equals(nodeName);
}
@SuppressWarnings("unchecked")
- private void verifyLegacyNode(Node<?> actual, LegacyNodeData expNodeData) {
+ private void verifyLegacyNode(final Node<?> actual, final LegacyNodeData expNodeData) {
assertNotNull("Actual Node is null", actual);
assertTrue("Expected CompositeNode instance", actual instanceof CompositeNode);
Collections.sort(unorderdChildData, new Comparator<LegacyNodeData>() {
@Override
- public int compare(LegacyNodeData arg1, LegacyNodeData arg2) {
- String str1 = arg1.nodeKey.getLocalName();
- if (!(arg1.nodeData instanceof List))
- str1 += arg1.nodeData; // add simple node value
-
- String str2 = arg2.nodeKey.getLocalName();
- if (!(arg2.nodeData instanceof List))
- str2 += arg2.nodeData; // add simple node value
-
- return str1.compareTo(str2);
+ public int compare(final LegacyNodeData arg1, final LegacyNodeData arg2) {
+ if (!(arg1.nodeData instanceof List) && !(arg2.nodeData instanceof List)) {
+ // if neither is a list, just compare them
+ String str1 = arg1.nodeKey.getLocalName() + arg1.nodeData;
+ String str2 = arg2.nodeKey.getLocalName() + arg2.nodeData;
+ return str1.compareTo(str2);
+ } else if (arg1.nodeData instanceof List && arg2.nodeData instanceof List) {
+ // if both are lists, first check their local name
+ String str1 = arg1.nodeKey.getLocalName();
+ String str2 = arg2.nodeKey.getLocalName();
+ if (!str1.equals(str2)) {
+ return str1.compareTo(str2);
+ } else {
+ // if local names are the same, then look at the list contents
+ List<LegacyNodeData> l1 = (List<LegacyNodeData>) arg1.nodeData;
+ List<LegacyNodeData> l2 = (List<LegacyNodeData>) arg2.nodeData;
+
+ if (l1.size() != l2.size()) {
+ // if the sizes are different, use that
+ return l2.size() - l1.size();
+ } else {
+ // lastly sort and recursively check the list contents
+ Collections.sort(l1, this);
+ Collections.sort(l2, this);
+
+ for (int i = 0 ; i < l1.size() ; i++) {
+ int diff = this.compare(l1.get(i), l2.get(i));
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return 0;
+ }
+ }
+ } else if( arg1.nodeData instanceof List ) {
+ return -1;
+ } else{
+ return 1;
+ }
}
});
Collections.sort(unorderedChildNodes, new Comparator<Node<?>>() {
@Override
- public int compare(Node<?> n1, Node<?> n2) {
- String str1 = n1.getKey().getLocalName();
- if (n1 instanceof SimpleNode)
- str1 += ((SimpleNode<?>) n1).getValue();
-
- String str2 = n2.getKey().getLocalName();
- if (n2 instanceof SimpleNode)
- str2 += ((SimpleNode<?>) n2).getValue();
-
- return str1.compareTo(str2);
+ public int compare(final Node<?> n1, final Node<?> n2) {
+ if (n1 instanceof SimpleNode && n2 instanceof SimpleNode) {
+ // if they're SimpleNodes just compare their strings
+ String str1 = n1.getKey().getLocalName() + ((SimpleNode<?>)n1).getValue();
+ String str2 = n2.getKey().getLocalName() + ((SimpleNode<?>)n2).getValue();
+ return str1.compareTo(str2);
+ } else if (n1 instanceof CompositeNode && n2 instanceof CompositeNode) {
+ // if they're CompositeNodes, things are more interesting
+ String str1 = n1.getKey().getLocalName();
+ String str2 = n2.getKey().getLocalName();
+ if (!str1.equals(str2)) {
+ // if their local names differ, return that difference
+ return str1.compareTo(str2);
+ } else {
+ // otherwise, we need to look at their contents
+ ArrayList<Node<?>> l1 = new ArrayList<Node<?>>( ((CompositeNode)n1).getValue() );
+ ArrayList<Node<?>> l2 = new ArrayList<Node<?>>( ((CompositeNode)n2).getValue() );
+
+ if (l1.size() != l2.size()) {
+ // if they have different numbers of things in them return that
+ return l2.size() - l1.size();
+ } else {
+ // otherwise, compare the individual elements, first sort them
+ Collections.sort(l1, this);
+ Collections.sort(l2, this);
+
+ // then compare them individually
+ for(int i = 0 ; i < l2.size() ; i++) {
+ int diff = this.compare(l1.get(i), l2.get(i));
+ if(diff != 0){
+ return diff;
+ }
+ }
+ return 0;
+ }
+ }
+ } else if (n1 instanceof CompositeNode && n2 instanceof SimpleNode) {
+ return -1;
+ } else if (n2 instanceof CompositeNode && n1 instanceof SimpleNode) {
+ return 1;
+ } else {
+ assertTrue("Expected either SimpleNodes CompositeNodes", false);
+ return 0;
+ }
}
});
assertEquals("Child node QName", expData.nodeKey, actualChild.getKey());
if (expData.nodeData instanceof List) { // List represents a
- // composite node
+ // composite node
verifyLegacyNode(actualChild, expData);
} else { // else a simple node
assertTrue("Expected SimpleNode instance", actualChild instanceof SimpleNode);
}
}
- private LegacyNodeData expectCompositeNode(QName key, LegacyNodeData... childData) {
+ private LegacyNodeData expectCompositeNode(final QName key, final LegacyNodeData... childData) {
return new LegacyNodeData(key, Lists.newArrayList(childData));
}
- private LegacyNodeData expectSimpleNode(QName key, Object value) {
+ private LegacyNodeData expectSimpleNode(final QName key, final Object value) {
return new LegacyNodeData(key, value);
}
}
Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedNodeEntry = normalizer
- .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(new InstanceIdentifier(
+ .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(InstanceIdentifier.create(
ImmutableList.<PathArgument> of(new NodeIdentifier(TEST_QNAME))), testBuilder.toInstance()));
verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME);
expectMapEntryNode(INNER_LIST_QNAME, NAME_QNAME, "inner-name3",
expectLeafNode(NAME_QNAME, "inner-name3"),
expectLeafNode(VALUE_QNAME, "inner-value3")),
- expectMapEntryNode(INNER_LIST_QNAME, NAME_QNAME, "inner-name2",
- expectLeafNode(NAME_QNAME, "inner-name2"),
- expectLeafNode(VALUE_QNAME, "inner-value2")),
- expectMapEntryNode(INNER_LIST_QNAME, NAME_QNAME, "inner-name1",
- expectLeafNode(NAME_QNAME, "inner-name1"),
- expectLeafNode(VALUE_QNAME, "inner-value1")))),
- expectMapEntryNode(
- OUTER_LIST_QNAME,
- ID_QNAME,
- 20,
- expectLeafNode(ID_QNAME, 20),
- expectChoiceNode(OUTER_CHOICE_QNAME, expectLeafNode(TWO_QNAME, "two"),
- expectLeafNode(THREE_QNAME, "three")))),
- expectUnkeyedListNode(
- UNKEYED_LIST_QNAME,
- expectUnkeyedListEntryNode(UNKEYED_LIST_QNAME,
- expectLeafNode(NAME_QNAME, "unkeyed-name1")),
- expectUnkeyedListEntryNode(UNKEYED_LIST_QNAME,
- expectLeafNode(NAME_QNAME, "unkeyed-name2")))));
+ expectMapEntryNode(INNER_LIST_QNAME, NAME_QNAME, "inner-name2",
+ expectLeafNode(NAME_QNAME, "inner-name2"),
+ expectLeafNode(VALUE_QNAME, "inner-value2")),
+ expectMapEntryNode(INNER_LIST_QNAME, NAME_QNAME, "inner-name1",
+ expectLeafNode(NAME_QNAME, "inner-name1"),
+ expectLeafNode(VALUE_QNAME, "inner-value1")))),
+ expectMapEntryNode(
+ OUTER_LIST_QNAME,
+ ID_QNAME,
+ 20,
+ expectLeafNode(ID_QNAME, 20),
+ expectChoiceNode(OUTER_CHOICE_QNAME, expectLeafNode(TWO_QNAME, "two"),
+ expectLeafNode(THREE_QNAME, "three")))),
+ expectUnkeyedListNode(
+ UNKEYED_LIST_QNAME,
+ expectUnkeyedListEntryNode(UNKEYED_LIST_QNAME,
+ expectLeafNode(NAME_QNAME, "unkeyed-name1")),
+ expectUnkeyedListEntryNode(UNKEYED_LIST_QNAME,
+ expectLeafNode(NAME_QNAME, "unkeyed-name2")))));
}
@Test
testBuilder.add(anyXmlLegacy);
Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedNodeEntry = normalizer
- .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(new InstanceIdentifier(
+ .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(InstanceIdentifier.create(
ImmutableList.<PathArgument> of(new NodeIdentifier(TEST_QNAME))), testBuilder.toInstance()));
verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME);
testBuilder.add(outerContBuilder.toInstance());
Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedNodeEntry = normalizer
- .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(new InstanceIdentifier(
+ .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(InstanceIdentifier.create(
ImmutableList.<PathArgument> of(new NodeIdentifier(TEST_QNAME))), testBuilder.toInstance()));
verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME);
expectContainerNode(TEST_QNAME, expectContainerNode(OUTER_CONTAINER_QNAME, expAugmentation)));
normalizedNodeEntry = normalizer.toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(
- new InstanceIdentifier(Lists.newArrayList(new NodeIdentifier(TEST_QNAME), new NodeIdentifier(
+ InstanceIdentifier.create(Lists.newArrayList(new NodeIdentifier(TEST_QNAME), new NodeIdentifier(
OUTER_CONTAINER_QNAME))), outerContBuilder.toInstance()));
verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME, OUTER_CONTAINER_QNAME,
}
Entry<InstanceIdentifier, NormalizedNode<?, ?>> normalizedNodeEntry = normalizer
- .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(new InstanceIdentifier(
+ .toNormalized(new AbstractMap.SimpleEntry<InstanceIdentifier, CompositeNode>(InstanceIdentifier.create(
ImmutableList.<PathArgument> of(new NodeIdentifier(TEST_QNAME))), testBuilder.toInstance()));
verifyNormalizedInstanceIdentifier(normalizedNodeEntry.getKey(), TEST_QNAME);
expectLeafSetEntryNode(UNORDERED_LEAF_LIST_QNAME, "unordered-value1"),
expectLeafSetEntryNode(UNORDERED_LEAF_LIST_QNAME, "unordered-value2"),
expectLeafSetEntryNode(UNORDERED_LEAF_LIST_QNAME, "unordered-value3")),
- expectOrderedLeafSetNode(ORDERED_LEAF_LIST_QNAME,
- expectLeafSetEntryNode(ORDERED_LEAF_LIST_QNAME, "ordered-value3"),
- expectLeafSetEntryNode(ORDERED_LEAF_LIST_QNAME, "ordered-value2"),
- expectLeafSetEntryNode(ORDERED_LEAF_LIST_QNAME, "ordered-value1"))));
+ expectOrderedLeafSetNode(ORDERED_LEAF_LIST_QNAME,
+ expectLeafSetEntryNode(ORDERED_LEAF_LIST_QNAME, "ordered-value3"),
+ expectLeafSetEntryNode(ORDERED_LEAF_LIST_QNAME, "ordered-value2"),
+ expectLeafSetEntryNode(ORDERED_LEAF_LIST_QNAME, "ordered-value1"))));
}
@SuppressWarnings("unchecked")
- private void verifyNormalizedNode(NormalizedNode<?, ?> actual, NormalizedNodeData expNodeData) {
+ private void verifyNormalizedNode(final NormalizedNode<?, ?> actual, final NormalizedNodeData expNodeData) {
Class<?> expNodeClass = expNodeData.nodeClass;
PathArgument expNodeID = expNodeData.nodeID;
NormalizedNodeData expChildData = expNodeClass.equals(UnkeyedListNode.class) ? expChildDataList
.remove(0) : expChildDataMap.remove(actualChild.getIdentifier());
- assertNotNull(
- "Unexpected child node " + actualChild.getClass() + " with identifier "
- + actualChild.getIdentifier() + " for parent node " + actual.getClass()
- + " with identifier " + actual.getIdentifier(), expChildData);
+ assertNotNull(
+ "Unexpected child node " + actualChild.getClass() + " with identifier "
+ + actualChild.getIdentifier() + " for parent node " + actual.getClass()
+ + " with identifier " + actual.getIdentifier(), expChildData);
- if (orderingMap != null) {
- assertEquals("Order index for child node " + actualChild.getIdentifier(),
- orderingMap.get(actualChild.getIdentifier()), Integer.valueOf(i));
- }
+ if (orderingMap != null) {
+ assertEquals("Order index for child node " + actualChild.getIdentifier(),
+ orderingMap.get(actualChild.getIdentifier()), Integer.valueOf(i));
+ }
- verifyNormalizedNode(actualChild, expChildData);
- i++;
+ verifyNormalizedNode(actualChild, expChildData);
+ i++;
}
if (expNodeClass.equals(UnkeyedListNode.class)) {
}
}
- private NormalizedNodeData expectOrderedLeafSetNode(QName nodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectOrderedLeafSetNode(final QName nodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), OrderedLeafSetNode.class,
Lists.newArrayList(childData));
}
- private NormalizedNodeData expectLeafSetNode(QName nodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectLeafSetNode(final QName nodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), LeafSetNode.class, Lists.newArrayList(childData));
}
- private NormalizedNodeData expectLeafSetEntryNode(QName nodeName, Object value) {
+ private NormalizedNodeData expectLeafSetEntryNode(final QName nodeName, final Object value) {
return new NormalizedNodeData(new NodeWithValue(nodeName, value), LeafSetEntryNode.class, value);
}
- private NormalizedNodeData expectUnkeyedListNode(QName nodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectUnkeyedListNode(final QName nodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), UnkeyedListNode.class,
Lists.newArrayList(childData));
}
- private NormalizedNodeData expectUnkeyedListEntryNode(QName nodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectUnkeyedListEntryNode(final QName nodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), UnkeyedListEntryNode.class,
Lists.newArrayList(childData));
}
- private NormalizedNodeData expectAugmentation(QName augmentedNodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectAugmentation(final QName augmentedNodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new AugmentationIdentifier(Sets.newHashSet(augmentedNodeName)),
AugmentationNode.class, Lists.newArrayList(childData));
}
- private NormalizedNodeData expectAnyXmlNode(QName nodeName, Object value) {
+ private NormalizedNodeData expectAnyXmlNode(final QName nodeName, final Object value) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), AnyXmlNode.class, value);
}
- private NormalizedNodeData expectContainerNode(QName nodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectContainerNode(final QName nodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), ContainerNode.class, Lists.newArrayList(childData));
}
- private NormalizedNodeData expectChoiceNode(QName nodeName, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectChoiceNode(final QName nodeName, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), ChoiceNode.class, Lists.newArrayList(childData));
}
- private NormalizedNodeData expectLeafNode(QName nodeName, Object value) {
+ private NormalizedNodeData expectLeafNode(final QName nodeName, final Object value) {
return new NormalizedNodeData(new NodeIdentifier(nodeName), LeafNode.class, value);
}
- private NormalizedNodeData expectMapEntryNode(QName nodeName, QName key, Object value,
- NormalizedNodeData... childData) {
+ private NormalizedNodeData expectMapEntryNode(final QName nodeName, final QName key, final Object value,
+ final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifierWithPredicates(nodeName, key, value), MapEntryNode.class,
Lists.newArrayList(childData));
}
- private NormalizedNodeData expectMapNode(QName key, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectMapNode(final QName key, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(key), MapNode.class, Lists.newArrayList(childData));
}
- private NormalizedNodeData expectOrderedMapNode(QName key, NormalizedNodeData... childData) {
+ private NormalizedNodeData expectOrderedMapNode(final QName key, final NormalizedNodeData... childData) {
return new NormalizedNodeData(new NodeIdentifier(key), OrderedMapNode.class, Lists.newArrayList(childData));
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+
+public class DataChangeListener extends UntypedActor {
+ @Override public void onReceive(Object message) throws Exception {
+ throw new UnsupportedOperationException("onReceive");
+ }
+
+ public static Props props() {
+ return Props.create(new Creator<DataChangeListener>() {
+ @Override
+ public DataChangeListener create() throws Exception {
+ return new DataChangeListener();
+ }
+
+ });
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorSelection;
+import org.opendaylight.controller.cluster.datastore.messages.DataChanged;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * DataChangeListenerProxy represents a single remote DataChangeListener
+ */
+public class DataChangeListenerProxy implements AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>{
+ private final ActorSelection dataChangeListenerActor;
+
+ public DataChangeListenerProxy(ActorSelection dataChangeListenerActor) {
+ this.dataChangeListenerActor = dataChangeListenerActor;
+ }
+
+ @Override public void onDataChanged(
+ AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ dataChangeListenerActor.tell(new DataChanged(change), null);
+ }
+}
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;
-import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistration;
-import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistrationReply;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistrationReply;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-public class ListenerRegistration extends UntypedActor{
+public class DataChangeListenerRegistration extends UntypedActor{
private final org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration;
- public ListenerRegistration(org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration) {
+ public DataChangeListenerRegistration(
+ org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration) {
this.registration = registration;
}
@Override
public void onReceive(Object message) throws Exception {
- if(message instanceof CloseListenerRegistration){
- closeListenerRegistration((CloseListenerRegistration) message);
+ if(message instanceof CloseDataChangeListenerRegistration){
+ closeListenerRegistration((CloseDataChangeListenerRegistration) message);
}
}
public static Props props(final org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration){
- return Props.create(new Creator<ListenerRegistration>(){
+ return Props.create(new Creator<DataChangeListenerRegistration>(){
@Override
- public ListenerRegistration create() throws Exception {
- return new ListenerRegistration(registration);
+ public DataChangeListenerRegistration create() throws Exception {
+ return new DataChangeListenerRegistration(registration);
}
});
}
- private void closeListenerRegistration(CloseListenerRegistration message){
+ private void closeListenerRegistration(CloseDataChangeListenerRegistration message){
registration.close();
- getSender().tell(new CloseListenerRegistrationReply(), getSelf());
+ getSender().tell(new CloseDataChangeListenerRegistrationReply(), getSelf());
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorSelection;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * ListenerRegistrationProxy acts as a proxy for a ListenerRegistration that was done on a remote shard
+ * <p>
+ * Registering a DataChangeListener on the Data Store creates a new instance of the ListenerRegistrationProxy
+ * The ListenerRegistrationProxy talks to a remote ListenerRegistration actor.
+ * </p>
+ */
+public class DataChangeListenerRegistrationProxy implements ListenerRegistration {
+ private final ActorSelection listenerRegistrationActor;
+ private final AsyncDataChangeListener listener;
+
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+ DataChangeListenerRegistrationProxy(
+ ActorSelection listenerRegistrationActor,
+ L listener) {
+ this.listenerRegistrationActor = listenerRegistrationActor;
+ this.listener = listener;
+ }
+
+ @Override
+ public Object getInstance() {
+ return listener;
+ }
+
+ @Override
+ public void close() {
+ listenerRegistrationActor.tell(new CloseDataChangeListenerRegistration(), null);
+ }
+}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
*/
-public class DistributedDataStore implements DOMStore {
+public class DistributedDataStore implements DOMStore, SchemaContextListener {
+
+ private static final Logger
+ LOG = LoggerFactory.getLogger(DistributedDataStore.class);
+
+
+ private final String type;
+ private final ActorContext actorContext;
+
+ public DistributedDataStore(ActorSystem actorSystem, String type) {
+ this(new ActorContext(actorSystem, actorSystem.actorOf(ShardManager.props(type))), type);
+ }
+
+ public DistributedDataStore(ActorContext actorContext, String type) {
+ this.type = type;
+ this.actorContext = actorContext;
+ }
+
@Override
- public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(InstanceIdentifier path, L listener, AsyncDataBroker.DataChangeScope scope) {
- return new ListenerRegistrationProxy();
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ InstanceIdentifier path, L listener,
+ AsyncDataBroker.DataChangeScope scope) {
+
+ ActorRef dataChangeListenerActor = actorContext.getActorSystem().actorOf(
+ DataChangeListener.props());
+
+ Object result = actorContext.executeShardOperation(Shard.DEFAULT_NAME,
+ new RegisterChangeListener(path, dataChangeListenerActor.path(),
+ AsyncDataBroker.DataChangeScope.BASE),
+ ActorContext.ASK_DURATION
+ );
+
+ RegisterChangeListenerReply reply = (RegisterChangeListenerReply) result;
+ return new DataChangeListenerRegistrationProxy(actorContext.actorSelection(reply.getListenerRegistrationPath()), listener);
}
+
+
@Override
public DOMStoreTransactionChain createTransactionChain() {
- return new TransactionChainProxy();
+ return new TransactionChainProxy(actorContext);
}
@Override
public DOMStoreReadTransaction newReadOnlyTransaction() {
- return new TransactionProxy();
+ return new TransactionProxy(actorContext, TransactionProxy.TransactionType.READ_ONLY);
}
@Override
public DOMStoreWriteTransaction newWriteOnlyTransaction() {
- return new TransactionProxy();
+ return new TransactionProxy(actorContext, TransactionProxy.TransactionType.WRITE_ONLY);
}
@Override
public DOMStoreReadWriteTransaction newReadWriteTransaction() {
- return new TransactionProxy();
+ return new TransactionProxy(actorContext, TransactionProxy.TransactionType.READ_WRITE);
+ }
+
+ @Override public void onGlobalContextUpdated(SchemaContext schemaContext) {
+ actorContext.getShardManager().tell(
+ new UpdateSchemaContext(schemaContext), null);
}
}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.cluster.datastore;
-
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-
-/**
- * ListenerRegistrationProxy acts as a proxy for a ListenerRegistration that was done on a remote shard
- *
- * Registering a DataChangeListener on the Data Store creates a new instance of the ListenerRegistrationProxy
- * The ListenerRegistrationProxy talks to a remote ListenerRegistration actor.
- */
-public class ListenerRegistrationProxy implements ListenerRegistration {
- @Override
- public Object getInstance() {
- throw new UnsupportedOperationException("getInstance");
- }
-
- @Override
- public void close() {
- throw new UnsupportedOperationException("close");
- }
-}
package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
+import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
+import akka.japi.Creator;
+import akka.persistence.Persistent;
import akka.persistence.UntypedProcessor;
+import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction;
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.cluster.datastore.modification.Modification;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
/**
- * A Shard represents a portion of the logical data tree
- * <p/>
+ * A Shard represents a portion of the logical data tree <br/>
+ * <p>
* Our Shard uses InMemoryDataStore as it's internal representation and delegates all requests it
- *
+ * </p>
*/
public class Shard extends UntypedProcessor {
- ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
+ public static final String DEFAULT_NAME = "default";
+
+ private final ListeningExecutorService storeExecutor =
+ MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
+
+ private final InMemoryDOMDataStore store;
+
+ private final Map<Modification, DOMStoreThreePhaseCommitCohort>
+ modificationToCohort = new HashMap<>();
+
+ private final LoggingAdapter log =
+ Logging.getLogger(getContext().system(), this);
+
+ private Shard(String name) {
+ store = new InMemoryDOMDataStore(name, storeExecutor);
+ }
+
+ public static Props props(final String name) {
+ return Props.create(new Creator<Shard>() {
+
+ @Override
+ public Shard create() throws Exception {
+ return new Shard(name);
+ }
+
+ });
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if (message instanceof CreateTransactionChain) {
+ createTransactionChain();
+ } else if (message instanceof RegisterChangeListener) {
+ registerChangeListener((RegisterChangeListener) message);
+ } else if (message instanceof UpdateSchemaContext) {
+ updateSchemaContext((UpdateSchemaContext) message);
+ } else if (message instanceof ForwardedCommitTransaction) {
+ handleForwardedCommit((ForwardedCommitTransaction) message);
+ } else if (message instanceof Persistent) {
+ commit((Persistent) message);
+ }
+ }
+
+ private void commit(Persistent message) {
+ Modification modification = (Modification) message.payload();
+ DOMStoreThreePhaseCommitCohort cohort =
+ modificationToCohort.remove(modification);
+ if (cohort == null) {
+ log.error(
+ "Could not find cohort for modification : " + modification);
+ return;
+ }
+ final ListenableFuture<Void> future = cohort.commit();
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ future.get();
+ sender.tell(new CommitTransactionReply(), self);
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e, "An exception happened when committing");
+ }
+ }
+ }, getContext().dispatcher());
+ }
- private final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", storeExecutor);
+ private void handleForwardedCommit(ForwardedCommitTransaction message) {
+ log.info("received forwarded transaction");
+ modificationToCohort
+ .put(message.getModification(), message.getCohort());
+ getSelf().forward(Persistent.create(message.getModification()),
+ getContext());
+ }
- LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+ private void updateSchemaContext(UpdateSchemaContext message) {
+ store.onGlobalContextUpdated(message.getSchemaContext());
+ }
+
+ private void registerChangeListener(
+ RegisterChangeListener registerChangeListener) {
+
+ ActorSelection dataChangeListenerPath = getContext()
+ .system().actorSelection(registerChangeListener.getDataChangeListenerPath());
+
+ AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>
+ listener = new DataChangeListenerProxy(dataChangeListenerPath);
+
+ org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+ registration =
+ store.registerChangeListener(registerChangeListener.getPath(),
+ listener, registerChangeListener.getScope());
+ ActorRef listenerRegistration =
+ getContext().actorOf(
+ DataChangeListenerRegistration.props(registration));
+ getSender()
+ .tell(new RegisterChangeListenerReply(listenerRegistration.path()),
+ getSelf());
+ }
- @Override
- public void onReceive(Object message) throws Exception {
- if (message instanceof CreateTransactionChain) {
- createTransactionChain();
- } else if(message instanceof RegisterChangeListener){
- registerChangeListener((RegisterChangeListener) message);
- } else if(message instanceof UpdateSchemaContext){
- updateSchemaContext((UpdateSchemaContext) message);
+ private void createTransactionChain() {
+ DOMStoreTransactionChain chain = store.createTransactionChain();
+ ActorRef transactionChain =
+ getContext().actorOf(ShardTransactionChain.props(chain));
+ getSender()
+ .tell(new CreateTransactionChainReply(transactionChain.path()),
+ getSelf());
}
- }
-
- private void updateSchemaContext(UpdateSchemaContext message) {
- store.onGlobalContextUpdated(message.getSchemaContext());
- }
-
- private void registerChangeListener(RegisterChangeListener registerChangeListener) {
- org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration =
- store.registerChangeListener(registerChangeListener.getPath(), registerChangeListener.getListener(), registerChangeListener.getScope());
- ActorRef listenerRegistration = getContext().actorOf(ListenerRegistration.props(registration));
- getSender().tell(new RegisterChangeListenerReply(listenerRegistration.path()), getSelf());
- }
-
- private void createTransactionChain() {
- DOMStoreTransactionChain chain = store.createTransactionChain();
- ActorRef transactionChain = getContext().actorOf(ShardTransactionChain.props(chain));
- getSender().tell(new CreateTransactionChainReply(transactionChain.path()), getSelf());
- }
}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorPath;
+import akka.actor.ActorRef;
import akka.actor.Address;
+import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
+import akka.japi.Creator;
import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
import java.util.HashMap;
import java.util.List;
/**
* The ShardManager has the following jobs,
- *
- * - Create all the local shard replicas that belong on this cluster member
- * - Find the primary replica for any given shard
- * - Engage in shard replica elections which decide which replica should be the primary
- *
- * Creation of Shard replicas
- * ==========================
- * When the ShardManager is constructed it reads the cluster configuration to find out which shard replicas
- * belong on this member. It finds out the name of the current cluster member from the Akka Clustering Service.
- *
- * Replica Elections
- * =================
- * The Shard Manager uses multiple cues to initiate election.
- * - When a member of the cluster dies
- * - When a local shard replica dies
- * - When a local shard replica comes alive
+ * <p>
+ * <li> Create all the local shard replicas that belong on this cluster member
+ * <li> Find the primary replica for any given shard
+ * <li> Engage in shard replica elections which decide which replica should be the primary
+ * </p>
+ * <p/>
+ * <h3>>Creation of Shard replicas</h3
+ * <p>
+ * When the ShardManager is constructed it reads the cluster configuration to find out which shard replicas
+ * belong on this member. It finds out the name of the current cluster member from the Akka Clustering Service.
+ * </p>
+ * <p/>
+ * <h3> Replica Elections </h3>
+ * <p/>
+ * <p>
+ * The Shard Manager uses multiple cues to initiate election.
+ * <li> When a member of the cluster dies
+ * <li> When a local shard replica dies
+ * <li> When a local shard replica comes alive
+ * </p>
*/
public class ShardManager extends UntypedActor {
- // Stores a mapping between a shard name and the address of the current primary
- private final Map<String, Address> shardNameToPrimaryAddress = new HashMap<>();
+ // Stores a mapping between a shard name and the address of the current primary
+ private final Map<String, Address> shardNameToPrimaryAddress = new HashMap<>();
+
+ // Stores a mapping between a member name and the address of the member
+ private final Map<String, Address> memberNameToAddress = new HashMap<>();
+
+ // Stores a mapping between the shard name and all the members on which a replica of that shard are available
+ private final Map<String, List<String>> shardNameToMembers = new HashMap<>();
- // Stores a mapping between a member name and the address of the member
- private final Map<String, Address> memberNameToAddress = new HashMap<>();
+ private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
- // Stores a mapping between the shard name and all the members on which a replica of that shard are available
- private final Map<String, List<String>> shardNameToMembers = new HashMap<>();
+ private final ActorPath defaultShardPath;
- LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+ /**
+ *
+ * @param type defines the kind of data that goes into shards created by this shard manager. Examples of type would be
+ * configuration or operational
+ */
+ private ShardManager(String type){
+ ActorRef actor = getContext().actorOf(Shard.props(Shard.DEFAULT_NAME + "-" + type));
+ defaultShardPath = actor.path();
+ }
- @Override
- public void onReceive(Object message) throws Exception {
- if(message instanceof FindPrimary ){
- FindPrimary msg = ((FindPrimary) message);
- getSender().tell(new PrimaryNotFound(msg.getShardName()), getSelf());
- }
+ public static Props props(final String type){
+ return Props.create(new Creator<ShardManager>(){
+
+ @Override
+ public ShardManager create() throws Exception {
+ return new ShardManager(type);
+ }
+ });
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if (message instanceof FindPrimary) {
+ FindPrimary msg = ((FindPrimary) message);
+ String shardName = msg.getShardName();
+ if(Shard.DEFAULT_NAME.equals(shardName)){
+ getSender().tell(new PrimaryFound(defaultShardPath.toString()), getSelf());
+ } else {
+ getSender().tell(new PrimaryNotFound(shardName), getSelf());
+ }
+ } else if(message instanceof UpdateSchemaContext){
+ // FIXME : Notify all local shards of a context change
+ getContext().system().actorSelection(defaultShardPath).forward(message, getContext());
}
+ }
+
+
}
import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
import org.opendaylight.controller.cluster.datastore.messages.WriteData;
import org.opendaylight.controller.cluster.datastore.messages.WriteDataReply;
+import org.opendaylight.controller.cluster.datastore.modification.CompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.DeleteModification;
+import org.opendaylight.controller.cluster.datastore.modification.ImmutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.MergeModification;
+import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
/**
* The ShardTransaction Actor represents a remote transaction
- *
+ *<p>
* The ShardTransaction Actor delegates all actions to DOMDataReadWriteTransaction
- *
+ *</p>
+ *<p>
* Even though the DOMStore and the DOMStoreTransactionChain implement multiple types of transactions
* the ShardTransaction Actor only works with read-write transactions. This is just to keep the logic simple. At this
* time there are no known advantages for creating a read-only or write-only transaction which may change over time
* at which point we can optimize things in the distributed store as well.
- *
- * Handles Messages
- * ----------------
- * {@link org.opendaylight.controller.cluster.datastore.messages.ReadData}
- * {@link org.opendaylight.controller.cluster.datastore.messages.WriteData}
- * {@link org.opendaylight.controller.cluster.datastore.messages.MergeData}
- * {@link org.opendaylight.controller.cluster.datastore.messages.DeleteData}
- * {@link org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction}
- * {@link org.opendaylight.controller.cluster.datastore.messages.CloseTransaction}
+ *</p>
+ *<p>
+ * Handles Messages <br/>
+ * ---------------- <br/>
+ * <li> {@link org.opendaylight.controller.cluster.datastore.messages.ReadData}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.messages.WriteData}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.messages.MergeData}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.messages.DeleteData}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.messages.CloseTransaction}
+ * </p>
*/
public class ShardTransaction extends UntypedActor {
+ private final ActorRef shardActor;
+
private final DOMStoreReadWriteTransaction transaction;
+ private final MutableCompositeModification modification = new MutableCompositeModification();
+
private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
- public ShardTransaction(DOMStoreReadWriteTransaction transaction) {
+ public ShardTransaction(DOMStoreReadWriteTransaction transaction, ActorRef shardActor) {
this.transaction = transaction;
+ this.shardActor = shardActor;
}
- public static Props props(final DOMStoreReadWriteTransaction transaction){
+ public static Props props(final DOMStoreReadWriteTransaction transaction, final ActorRef shardActor){
return Props.create(new Creator<ShardTransaction>(){
@Override
public ShardTransaction create() throws Exception {
- return new ShardTransaction(transaction);
+ return new ShardTransaction(transaction, shardActor);
}
});
}
readyTransaction((ReadyTransaction) message);
} else if(message instanceof CloseTransaction){
closeTransaction((CloseTransaction) message);
+ } else if(message instanceof GetCompositedModification){
+ // This is here for testing only
+ getSender().tell(new GetCompositeModificationReply(new ImmutableCompositeModification(modification)), getSelf());
}
}
private void writeData(WriteData message){
+ modification.addModification(new WriteModification(message.getPath(), message.getData()));
transaction.write(message.getPath(), message.getData());
getSender().tell(new WriteDataReply(), getSelf());
}
private void mergeData(MergeData message){
+ modification.addModification(new MergeModification(message.getPath(), message.getData()));
transaction.merge(message.getPath(), message.getData());
getSender().tell(new MergeDataReply(), getSelf());
}
private void deleteData(DeleteData message){
+ modification.addModification(new DeleteModification(message.getPath()));
transaction.delete(message.getPath());
getSender().tell(new DeleteDataReply(), getSelf());
}
private void readyTransaction(ReadyTransaction message){
DOMStoreThreePhaseCommitCohort cohort = transaction.ready();
- ActorRef cohortActor = getContext().actorOf(ThreePhaseCommitCohort.props(cohort));
+ ActorRef cohortActor = getContext().actorOf(ThreePhaseCommitCohort.props(cohort, shardActor, modification));
getSender().tell(new ReadyTransactionReply(cohortActor.path()), getSelf());
}
transaction.close();
getSender().tell(new CloseTransactionReply(), getSelf());
}
+
+
+ // These classes are in here for test purposes only
+
+ static class GetCompositedModification {
+
+ }
+
+ static class GetCompositeModificationReply {
+ private final CompositeModification modification;
+
+
+ GetCompositeModificationReply(CompositeModification modification) {
+ this.modification = modification;
+ }
+
+
+ public CompositeModification getModification() {
+ return modification;
+ }
+ }
}
public void onReceive(Object message) throws Exception {
if(message instanceof CreateTransaction){
DOMStoreReadWriteTransaction transaction = chain.newReadWriteTransaction();
- ActorRef transactionActor = getContext().actorOf(ShardTransaction.props(transaction));
+ ActorRef transactionActor = getContext().actorOf(ShardTransaction.props(transaction, getContext().parent()));
getSender().tell(new CreateTransactionReply(transactionActor.path()), getSelf());
} else if (message instanceof CloseTransactionChain){
chain.close();
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
import akka.japi.Creator;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransactionReply;
+import org.opendaylight.controller.cluster.datastore.modification.CompositeModification;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import java.util.concurrent.ExecutionException;
+
public class ThreePhaseCommitCohort extends UntypedActor{
private final DOMStoreThreePhaseCommitCohort cohort;
+ private final ActorRef shardActor;
+ private final CompositeModification modification;
- public ThreePhaseCommitCohort(DOMStoreThreePhaseCommitCohort cohort) {
-
+ public ThreePhaseCommitCohort(DOMStoreThreePhaseCommitCohort cohort, ActorRef shardActor, CompositeModification modification) {
this.cohort = cohort;
+ this.shardActor = shardActor;
+ this.modification = modification;
}
- @Override
- public void onReceive(Object message) throws Exception {
- throw new UnsupportedOperationException("onReceive");
- }
+ private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
- public static Props props(final DOMStoreThreePhaseCommitCohort cohort) {
+ public static Props props(final DOMStoreThreePhaseCommitCohort cohort, final ActorRef shardActor, final CompositeModification modification) {
return Props.create(new Creator<ThreePhaseCommitCohort>(){
@Override
public ThreePhaseCommitCohort create() throws Exception {
- return new ThreePhaseCommitCohort(cohort);
+ return new ThreePhaseCommitCohort(cohort, shardActor, modification);
}
});
}
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if(message instanceof CanCommitTransaction){
+ canCommit((CanCommitTransaction) message);
+ } else if(message instanceof PreCommitTransaction) {
+ preCommit((PreCommitTransaction) message);
+ } else if(message instanceof CommitTransaction){
+ commit((CommitTransaction) message);
+ } else if (message instanceof AbortTransaction){
+ abort((AbortTransaction) message);
+ }
+ }
+
+ private void abort(AbortTransaction message) {
+ final ListenableFuture<Void> future = cohort.abort();
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ future.get();
+ sender.tell(new AbortTransactionReply(), self);
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e, "An exception happened when aborting");
+ }
+ }
+ }, getContext().dispatcher());
+ }
+
+ private void commit(CommitTransaction message) {
+ // Forward the commit to the shard
+ log.info("Commit transaction now + " + shardActor);
+ shardActor.forward(new ForwardedCommitTransaction(cohort, modification), getContext());
+
+ }
+
+ private void preCommit(PreCommitTransaction message) {
+ final ListenableFuture<Void> future = cohort.preCommit();
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ future.get();
+ sender.tell(new PreCommitTransactionReply(), self);
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e, "An exception happened when preCommitting");
+ }
+ }
+ }, getContext().dispatcher());
+
+ }
+
+ private void canCommit(CanCommitTransaction message) {
+ final ListenableFuture<Boolean> future = cohort.canCommit();
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Boolean canCommit = future.get();
+ sender.tell(new CanCommitTransactionReply(canCommit), self);
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e, "An exception happened when aborting");
+ }
+ }
+ }, getContext().dispatcher());
+
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorPath;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * ThreePhaseCommitCohortProxy represents a set of remote cohort proxies
+ */
+public class ThreePhaseCommitCohortProxy implements
+ DOMStoreThreePhaseCommitCohort{
+
+ private final List<ActorPath> cohortPaths;
+
+ public ThreePhaseCommitCohortProxy(List<ActorPath> cohortPaths) {
+
+ this.cohortPaths = cohortPaths;
+ }
+
+ @Override public ListenableFuture<Boolean> canCommit() {
+ throw new UnsupportedOperationException("canCommit");
+ }
+
+ @Override public ListenableFuture<Void> preCommit() {
+ throw new UnsupportedOperationException("preCommit");
+ }
+
+ @Override public ListenableFuture<Void> abort() {
+ throw new UnsupportedOperationException("abort");
+ }
+
+ @Override public ListenableFuture<Void> commit() {
+ throw new UnsupportedOperationException("commit");
+ }
+
+ public List<ActorPath> getCohortPaths() {
+ return Collections.unmodifiableList(this.cohortPaths);
+ }
+}
package org.opendaylight.controller.cluster.datastore;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
* TransactionChainProxy acts as a proxy for a DOMStoreTransactionChain created on a remote shard
*/
public class TransactionChainProxy implements DOMStoreTransactionChain{
+ private final ActorContext actorContext;
+
+ public TransactionChainProxy(ActorContext actorContext) {
+ this.actorContext = actorContext;
+ }
+
@Override
public DOMStoreReadTransaction newReadOnlyTransaction() {
- throw new UnsupportedOperationException("newReadOnlyTransaction");
+ return new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
}
@Override
public DOMStoreReadWriteTransaction newReadWriteTransaction() {
- throw new UnsupportedOperationException("newReadWriteTransaction");
+ return new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.WRITE_ONLY);
}
@Override
public DOMStoreWriteTransaction newWriteOnlyTransaction() {
- throw new UnsupportedOperationException("newWriteOnlyTransaction");
+ return new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_WRITE);
}
@Override
public void close() {
- throw new UnsupportedOperationException("close");
+ throw new UnsupportedOperationException("close - not sure what to do here?");
}
}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorPath;
+import akka.actor.ActorSelection;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicLong;
+
/**
* TransactionProxy acts as a proxy for one or more transactions that were created on a remote shard
- *
+ * <p>
* Creating a transaction on the consumer side will create one instance of a transaction proxy. If during
* the transaction reads and writes are done on data that belongs to different shards then a separate transaction will
* be created on each of those shards by the TransactionProxy
- *
+ *</p>
+ * <p>
* The TransactionProxy does not make any guarantees about atomicity or order in which the transactions on the various
* shards will be executed.
- *
+ * </p>
*/
public class TransactionProxy implements DOMStoreReadWriteTransaction {
+
+ public enum TransactionType {
+ READ_ONLY,
+ WRITE_ONLY,
+ READ_WRITE
+ }
+
+ private static final AtomicLong counter = new AtomicLong();
+
+ private final TransactionType readOnly;
+ private final ActorContext actorContext;
+ private final Map<String, ActorSelection> remoteTransactionPaths = new HashMap<>();
+ private final String identifier;
+
+ public TransactionProxy(
+ ActorContext actorContext,
+ TransactionType readOnly) {
+
+ this.identifier = "transaction-" + counter.getAndIncrement();
+ this.readOnly = readOnly;
+ this.actorContext = actorContext;
+
+ Object response = actorContext.executeShardOperation(Shard.DEFAULT_NAME, new CreateTransaction(), ActorContext.ASK_DURATION);
+ if(response instanceof CreateTransactionReply){
+ CreateTransactionReply reply = (CreateTransactionReply) response;
+ remoteTransactionPaths.put(Shard.DEFAULT_NAME, actorContext.actorSelection(reply.getTransactionPath()));
+ }
+ }
+
@Override
- public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(InstanceIdentifier path) {
- throw new UnsupportedOperationException("read");
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+
+ Callable<Optional<NormalizedNode<?,?>>> call = new Callable() {
+
+ @Override public Optional<NormalizedNode<?,?>> call() throws Exception {
+ Object response = actorContext
+ .executeRemoteOperation(remoteTransaction, new ReadData(path),
+ ActorContext.ASK_DURATION);
+ if(response instanceof ReadDataReply){
+ ReadDataReply reply = (ReadDataReply) response;
+ //FIXME : A cast should not be required here ???
+ return (Optional<NormalizedNode<?, ?>>) Optional.of(reply.getNormalizedNode());
+ }
+
+ return Optional.absent();
+ }
+ };
+
+ ListenableFutureTask<Optional<NormalizedNode<?, ?>>>
+ future = ListenableFutureTask.create(call);
+
+ //FIXME : Use a thread pool here
+ Executors.newSingleThreadExecutor().submit(future);
+
+ return future;
}
@Override
public void write(InstanceIdentifier path, NormalizedNode<?, ?> data) {
- throw new UnsupportedOperationException("write");
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+ remoteTransaction.tell(new WriteData(path, data), null);
}
@Override
public void merge(InstanceIdentifier path, NormalizedNode<?, ?> data) {
- throw new UnsupportedOperationException("merge");
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+ remoteTransaction.tell(new MergeData(path, data), null);
}
@Override
public void delete(InstanceIdentifier path) {
- throw new UnsupportedOperationException("delete");
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+ remoteTransaction.tell(new DeleteData(path), null);
}
@Override
public DOMStoreThreePhaseCommitCohort ready() {
- throw new UnsupportedOperationException("ready");
+ List<ActorPath> cohortPaths = new ArrayList<>();
+
+ for(ActorSelection remoteTransaction : remoteTransactionPaths.values()) {
+ Object result = actorContext.executeRemoteOperation(remoteTransaction,
+ new ReadyTransaction(),
+ ActorContext.ASK_DURATION
+ );
+
+ if(result instanceof ReadyTransactionReply){
+ ReadyTransactionReply reply = (ReadyTransactionReply) result;
+ cohortPaths.add(reply.getCohortPath());
+ }
+ }
+
+ return new ThreePhaseCommitCohortProxy(cohortPaths);
}
@Override
public Object getIdentifier() {
- throw new UnsupportedOperationException("getIdentifier");
+ return this.identifier;
}
@Override
public void close() {
- throw new UnsupportedOperationException("close");
+ for(ActorSelection remoteTransaction : remoteTransactionPaths.values()) {
+ remoteTransaction.tell(new CloseTransaction(), null);
+ }
+ }
+
+ private ActorSelection remoteTransactionFromIdentifier(InstanceIdentifier path){
+ String shardName = shardNameFromIdentifier(path);
+ return remoteTransactionPaths.get(shardName);
+ }
+
+ private String shardNameFromIdentifier(InstanceIdentifier path){
+ return Shard.DEFAULT_NAME;
}
}
package org.opendaylight.controller.cluster.datastore.messages;
-public class CloseListenerRegistration {
+public class AbortTransaction {
}
package org.opendaylight.controller.cluster.datastore.messages;
-public class CloseListenerRegistrationReply {
+public class AbortTransactionReply {
}
/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ * 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.connector.remoterpc;
-
-public interface RemoteRpcServer extends AutoCloseable {
+package org.opendaylight.controller.cluster.datastore.messages;
+public class CanCommitTransaction {
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CanCommitTransactionReply {
+ private final Boolean canCommit;
+
+ public CanCommitTransactionReply(Boolean canCommit) {
+ this.canCommit = canCommit;
+ }
+
+ public Boolean getCanCommit() {
+ return canCommit;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseDataChangeListenerRegistration {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseDataChangeListenerRegistrationReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CommitTransaction {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CommitTransactionReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class DataChanged {
+ private final AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>>
+ change;
+
+ public DataChanged(
+ AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ this.change = change;
+ }
+
+ public AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> getChange() {
+ return change;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.controller.cluster.datastore.modification.Modification;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+
+public class ForwardedCommitTransaction {
+ private final DOMStoreThreePhaseCommitCohort cohort;
+ private final Modification modification;
+
+ public ForwardedCommitTransaction(DOMStoreThreePhaseCommitCohort cohort, Modification modification){
+ this.cohort = cohort;
+ this.modification = modification;
+ }
+
+ public DOMStoreThreePhaseCommitCohort getCohort() {
+ return cohort;
+ }
+
+ public Modification getModification() {
+ return modification;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class PreCommitTransaction {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class PreCommitTransactionReply {
+}
package org.opendaylight.controller.cluster.datastore.messages;
public class PrimaryFound {
+ private final String primaryPath;
+
+ public PrimaryFound(String primaryPath) {
+ this.primaryPath = primaryPath;
+ }
+
+ public String getPrimaryPath() {
+ return primaryPath;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PrimaryFound that = (PrimaryFound) o;
+
+ if (!primaryPath.equals(that.primaryPath)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return primaryPath.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "PrimaryFound{" +
+ "primaryPath='" + primaryPath + '\'' +
+ '}';
+ }
+
+
}
import akka.actor.ActorPath;
public class ReadyTransactionReply {
- private final ActorPath path;
+ private final ActorPath cohortPath;
- public ReadyTransactionReply(ActorPath path) {
+ public ReadyTransactionReply(ActorPath cohortPath) {
- this.path = path;
+ this.cohortPath = cohortPath;
}
- public ActorPath getPath() {
- return path;
+ public ActorPath getCohortPath() {
+ return cohortPath;
}
}
package org.opendaylight.controller.cluster.datastore.messages;
+import akka.actor.ActorPath;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class RegisterChangeListener {
- private final InstanceIdentifier path;
- private final AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener;
- private final AsyncDataBroker.DataChangeScope scope;
+ private final InstanceIdentifier path;
+ private final ActorPath dataChangeListenerPath;
+ private final AsyncDataBroker.DataChangeScope scope;
- public RegisterChangeListener(InstanceIdentifier path, AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener, AsyncDataBroker.DataChangeScope scope) {
- this.path = path;
- this.listener = listener;
- this.scope = scope;
- }
+ public RegisterChangeListener(InstanceIdentifier path,
+ ActorPath dataChangeListenerPath,
+ AsyncDataBroker.DataChangeScope scope) {
+ this.path = path;
+ this.dataChangeListenerPath = dataChangeListenerPath;
+ this.scope = scope;
+ }
- public InstanceIdentifier getPath() {
- return path;
- }
+ public InstanceIdentifier getPath() {
+ return path;
+ }
- public AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> getListener() {
- return listener;
- }
- public AsyncDataBroker.DataChangeScope getScope() {
- return scope;
- }
+ public AsyncDataBroker.DataChangeScope getScope() {
+ return scope;
+ }
+
+ public ActorPath getDataChangeListenerPath() {
+ return dataChangeListenerPath;
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+import java.io.Serializable;
+
+/**
+ * Base class to be used for all simple modifications that can be applied to a DOMStoreTransaction
+ */
+public abstract class AbstractModification implements Modification,
+ Serializable {
+
+ private static final long serialVersionUID = 1638042650152084457L;
+
+ protected final InstanceIdentifier path;
+
+ protected AbstractModification(InstanceIdentifier path) {
+ this.path = path;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import java.util.List;
+
+/**
+ * CompositeModification contains a list of modifications that need to be applied to the DOMStore
+ * <p>
+ * A CompositeModification gets stored in the transaction log for a Shard. During recovery when the transaction log
+ * is being replayed a DOMStoreWriteTransaction could be created and a CompositeModification could be applied to it.
+ * </p>
+ */
+public interface CompositeModification extends Modification {
+ /**
+ * Get a list of Modifications contained by this Composite
+ * @return
+ */
+ List<Modification> getModifications();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * DeleteModification store all the parameters required to delete a path from the data tree
+ */
+public class DeleteModification extends AbstractModification {
+ public DeleteModification(InstanceIdentifier path) {
+ super(path);
+ }
+
+ @Override
+ public void apply(DOMStoreWriteTransaction transaction) {
+ transaction.delete(path);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+import java.util.List;
+
+public class ImmutableCompositeModification implements CompositeModification{
+
+ private final CompositeModification modification;
+
+ public ImmutableCompositeModification(CompositeModification modification){
+ this.modification = modification;
+ }
+
+ @Override
+ public List<Modification> getModifications() {
+ return modification.getModifications();
+ }
+
+ @Override
+ public void apply(DOMStoreWriteTransaction transaction) {
+ modification.apply(transaction);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * MergeModification stores all the parameters required to merge data into the specified path
+ */
+public class MergeModification extends AbstractModification{
+ private final NormalizedNode data;
+
+
+ public MergeModification(InstanceIdentifier path, NormalizedNode data) {
+ super(path);
+ this.data = data;
+ }
+
+ @Override
+ public void apply(DOMStoreWriteTransaction transaction) {
+ transaction.merge(path, data);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+/**
+ * Represents a modification to the data store.
+ * <p>
+ * Simple modifications can be of type,
+ * <li> {@link org.opendaylight.controller.cluster.datastore.modification.WriteModification}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.modification.MergeModification}
+ * <li> {@link org.opendaylight.controller.cluster.datastore.modification.DeleteModification}
+ * </p>
+ *
+ * <p>
+ * Modifications can in turn be lumped into a single {@link org.opendaylight.controller.cluster.datastore.modification.CompositeModification}
+ * which can then be applied to a write transaction
+ * </p>
+ */
+public interface Modification {
+ /**
+ * Apply the modification to the specified transaction
+ * @param transaction
+ */
+ void apply(DOMStoreWriteTransaction transaction);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * MutableCompositeModification is just a mutable version of a
+ * CompositeModification {@link org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification#addModification(Modification)}
+ */
+public class MutableCompositeModification
+ implements CompositeModification, Serializable {
+
+ private static final long serialVersionUID = 1163377899140186790L;
+
+ private final List<Modification> modifications = new ArrayList<>();
+
+ @Override
+ public void apply(DOMStoreWriteTransaction transaction) {
+ for (Modification modification : modifications) {
+ modification.apply(transaction);
+ }
+ }
+
+ /**
+ * Add a new Modification to the list of Modifications represented by this
+ * composite
+ *
+ * @param modification
+ */
+ public void addModification(Modification modification) {
+ modifications.add(modification);
+ }
+
+ public List<Modification> getModifications() {
+ return Collections.unmodifiableList(modifications);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * WriteModification stores all the parameters required to write data to the specified path
+ */
+public class WriteModification extends AbstractModification {
+
+ private final NormalizedNode data;
+
+ public WriteModification(InstanceIdentifier path, NormalizedNode data) {
+ super(path);
+ this.data = data;
+ }
+
+ @Override
+ public void apply(DOMStoreWriteTransaction transaction) {
+ transaction.write(path, data);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import akka.actor.ActorPath;
+import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
+import akka.util.Timeout;
+import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.Duration;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.util.concurrent.TimeUnit;
+
+import static akka.pattern.Patterns.ask;
+
+/**
+ * The ActorContext class contains utility methods which could be used by
+ * non-actors (like DistributedDataStore) to work with actors a little more
+ * easily. An ActorContext can be freely passed around to local object instances
+ * but should not be passed to actors especially remote actors
+ */
+public class ActorContext {
+ private static final Logger
+ LOG = LoggerFactory.getLogger(ActorContext.class);
+
+ public static final FiniteDuration ASK_DURATION = Duration.create(5, TimeUnit.SECONDS);
+ public static final Duration AWAIT_DURATION = Duration.create(5, TimeUnit.SECONDS);
+
+ private final ActorSystem actorSystem;
+ private final ActorRef shardManager;
+
+ public ActorContext(ActorSystem actorSystem, ActorRef shardManager){
+ this.actorSystem = actorSystem;
+ this.shardManager = shardManager;
+ }
+
+ public ActorSystem getActorSystem() {
+ return actorSystem;
+ }
+
+ public ActorRef getShardManager() {
+ return shardManager;
+ }
+
+ public ActorSelection actorSelection(String actorPath){
+ return actorSystem.actorSelection(actorPath);
+ }
+
+ public ActorSelection actorSelection(ActorPath actorPath){
+ return actorSystem.actorSelection(actorPath);
+ }
+
+
+ /**
+ * Finds the primary for a given shard
+ *
+ * @param shardName
+ * @return
+ */
+ public ActorSelection findPrimary(String shardName) {
+ Object result = executeLocalOperation(shardManager,
+ new FindPrimary(shardName), ASK_DURATION);
+
+ if(result instanceof PrimaryFound){
+ PrimaryFound found = (PrimaryFound) result;
+
+ LOG.error("Primary found {}", found.getPrimaryPath());
+
+ return actorSystem.actorSelection(found.getPrimaryPath());
+ }
+ throw new RuntimeException("primary was not found");
+ }
+
+ /**
+ * Executes an operation on a local actor and wait for it's response
+ * @param actor
+ * @param message
+ * @param duration
+ * @return The response of the operation
+ */
+ public Object executeLocalOperation(ActorRef actor, Object message,
+ FiniteDuration duration){
+ Future<Object> future =
+ ask(actor, message, new Timeout(duration));
+
+ try {
+ return Await.result(future, AWAIT_DURATION);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Execute an operation on a remote actor and wait for it's response
+ * @param actor
+ * @param message
+ * @param duration
+ * @return
+ */
+ public Object executeRemoteOperation(ActorSelection actor, Object message,
+ FiniteDuration duration){
+ Future<Object> future =
+ ask(actor, message, new Timeout(duration));
+
+ try {
+ return Await.result(future, AWAIT_DURATION);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Execute an operation on the primary for a given shard
+ * <p>
+ * This method first finds the primary for a given shard ,then sends
+ * the message to the remote shard and waits for a response
+ * </p>
+ * @param shardName
+ * @param message
+ * @param duration
+ * @throws java.lang.RuntimeException when a primary is not found or if the message to the remote shard fails or times out
+ *
+ * @return
+ */
+ public Object executeShardOperation(String shardName, Object message, FiniteDuration duration){
+ ActorSelection primary = findPrimary(shardName);
+
+ return executeRemoteOperation(primary, message, duration);
+ }
+
+}
package org.opendaylight.controller.config.yang.config.distributed_datastore_provider;
+import akka.actor.ActorSystem;
import org.opendaylight.controller.cluster.datastore.DistributedDataStore;
public class DistributedDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedDataStoreProviderModule {
- public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
+ public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.distributed_datastore_provider.DistributedDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ ActorSystem actorSystem = ActorSystem.create("opendaylight-cluster");
+ final DistributedDataStore configurationStore = new DistributedDataStore(actorSystem, "config");
+ final DistributedDataStore operationalStore = new DistributedDataStore(actorSystem, "operational");
+
+ final class AutoCloseableDistributedDataStore implements AutoCloseable {
+
+ @Override
+ public void close() throws Exception {
+ }
}
- public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.distributed_datastore_provider.DistributedDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- public void customValidation() {
- // add custom validation form module attributes here.
- }
-
- @Override
- public java.lang.AutoCloseable createInstance() {
- new DistributedDataStore();
-
- final class AutoCloseableDistributedDataStore implements AutoCloseable {
-
- @Override
- public void close() throws Exception {
-
- }
- }
-
- return new AutoCloseableDistributedDataStore();
- }
+ return new AutoCloseableDistributedDataStore();
+ }
}
private static ActorSystem system;
@BeforeClass
- public static void setUp(){
+ public static void setUpClass(){
system = ActorSystem.create("test");
}
@AfterClass
- public static void tearDown(){
+ public static void tearDownClass(){
JavaTestKit.shutdownActorSystem(system);
system = null;
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorPath;
+import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.cluster.datastore.messages.WriteDataReply;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class BasicIntegrationTest extends AbstractActorTest {
+
+ @Test
+ public void integrationTest() {
+ // This test will
+ // - create a Shard
+ // - initiate a transaction
+ // - write something
+ // - read the transaction for commit
+ // - commit the transaction
+
+
+ new JavaTestKit(getSystem()) {{
+ final Props props = Shard.props("config");
+ final ActorRef shard = getSystem().actorOf(props);
+
+ new Within(duration("5 seconds")) {
+ protected void run() {
+
+ shard.tell(
+ new UpdateSchemaContext(TestModel.createTestContext()),
+ getRef());
+
+ shard.tell(new CreateTransactionChain(), getRef());
+
+ final ActorSelection transactionChain =
+ new ExpectMsg<ActorSelection>("match hint") {
+ protected ActorSelection match(Object in) {
+ if (in instanceof CreateTransactionChainReply) {
+ ActorPath transactionChainPath =
+ ((CreateTransactionChainReply) in)
+ .getTransactionChainPath();
+ return getSystem()
+ .actorSelection(transactionChainPath);
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ Assert.assertNotNull(transactionChain);
+
+ transactionChain.tell(new CreateTransaction(), getRef());
+
+ final ActorSelection transaction =
+ new ExpectMsg<ActorSelection>("match hint") {
+ protected ActorSelection match(Object in) {
+ if (in instanceof CreateTransactionReply) {
+ ActorPath transactionPath =
+ ((CreateTransactionReply) in)
+ .getTransactionPath();
+ return getSystem()
+ .actorSelection(transactionPath);
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ Assert.assertNotNull(transaction);
+
+ transaction.tell(new WriteData(TestModel.TEST_PATH,
+ ImmutableNodes.containerNode(TestModel.TEST_QNAME)),
+ getRef());
+
+ Boolean writeDone = new ExpectMsg<Boolean>("match hint") {
+ protected Boolean match(Object in) {
+ if (in instanceof WriteDataReply) {
+ return true;
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ Assert.assertTrue(writeDone);
+
+ transaction.tell(new ReadyTransaction(), getRef());
+
+ final ActorSelection cohort =
+ new ExpectMsg<ActorSelection>("match hint") {
+ protected ActorSelection match(Object in) {
+ if (in instanceof ReadyTransactionReply) {
+ ActorPath cohortPath =
+ ((ReadyTransactionReply) in)
+ .getCohortPath();
+ return getSystem()
+ .actorSelection(cohortPath);
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ Assert.assertNotNull(cohort);
+
+ cohort.tell(new PreCommitTransaction(), getRef());
+
+ Boolean preCommitDone =
+ new ExpectMsg<Boolean>("match hint") {
+ protected Boolean match(Object in) {
+ if (in instanceof PreCommitTransactionReply) {
+ return true;
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ Assert.assertTrue(preCommitDone);
+
+ cohort.tell(new CommitTransaction(), getRef());
+
+ final Boolean commitDone =
+ new ExpectMsg<Boolean>("match hint") {
+ protected Boolean match(Object in) {
+ if (in instanceof CommitTransactionReply) {
+ return true;
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ Assert.assertTrue(commitDone);
+
+ }
+
+
+ };
+ }};
+
+
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.DataChanged;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DataChangeListenerProxyTest extends AbstractActorTest {
+
+ private static class MockDataChangeEvent implements
+ AsyncDataChangeEvent<InstanceIdentifier,NormalizedNode<?,?>> {
+
+ @Override
+ public Map<InstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+ throw new UnsupportedOperationException("getCreatedData");
+ }
+
+ @Override
+ public Map<InstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+ throw new UnsupportedOperationException("getUpdatedData");
+ }
+
+ @Override public Set<InstanceIdentifier> getRemovedPaths() {
+ throw new UnsupportedOperationException("getRemovedPaths");
+ }
+
+ @Override
+ public Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> getOriginalData() {
+ throw new UnsupportedOperationException("getOriginalData");
+ }
+
+ @Override public NormalizedNode<?, ?> getOriginalSubtree() {
+ throw new UnsupportedOperationException("getOriginalSubtree");
+ }
+
+ @Override public NormalizedNode<?, ?> getUpdatedSubtree() {
+ throw new UnsupportedOperationException("getUpdatedSubtree");
+ }
+ }
+
+ @Test
+ public void testOnDataChanged() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ DataChangeListenerProxy dataChangeListenerProxy =
+ new DataChangeListenerProxy(
+ getSystem().actorSelection(actorRef.path()));
+
+ dataChangeListenerProxy.onDataChanged(new MockDataChangeEvent());
+
+ //Check if it was received by the remote actor
+ ActorContext
+ testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof DataChanged);
+
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.util.List;
+
+public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest{
+
+ private static class MockDataChangeListener implements
+ AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ @Override public void onDataChanged(
+ AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ throw new UnsupportedOperationException("onDataChanged");
+ }
+ }
+
+ @Test
+ public void testGetInstance() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ MockDataChangeListener listener =
+ new MockDataChangeListener();
+ DataChangeListenerRegistrationProxy proxy =
+ new DataChangeListenerRegistrationProxy(
+ getSystem().actorSelection(actorRef.path()),
+ listener);
+
+ Assert.assertEquals(listener, proxy.getInstance());
+
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ DataChangeListenerRegistrationProxy proxy =
+ new DataChangeListenerRegistrationProxy(
+ getSystem().actorSelection(actorRef.path()),
+ new MockDataChangeListener());
+
+ proxy.close();
+
+ //Check if it was received by the remote actor
+ ActorContext
+ testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof CloseDataChangeListenerRegistration);
+ }
+}
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Test;
-import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistration;
-import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistrationReply;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistrationReply;
import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import static org.junit.Assert.assertEquals;
-public class ListenerRegistrationTest extends AbstractActorTest {
+public class DataChangeListenerRegistrationTest extends AbstractActorTest {
private static ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor());
private static final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", storeExecutor);
@Test
public void testOnReceiveCloseListenerRegistration() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ListenerRegistration.props(store.registerChangeListener(TestModel.TEST_PATH, noOpDataChangeListener(), AsyncDataBroker.DataChangeScope.BASE));
+ final Props props = DataChangeListenerRegistration.props(store
+ .registerChangeListener(TestModel.TEST_PATH, noOpDataChangeListener(),
+ AsyncDataBroker.DataChangeScope.BASE));
final ActorRef subject = getSystem().actorOf(props, "testCloseListenerRegistration");
new Within(duration("1 seconds")) {
protected void run() {
- subject.tell(new CloseListenerRegistration(), getRef());
+ subject.tell(new CloseDataChangeListenerRegistration(), getRef());
final String out = new ExpectMsg<String>("match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
- if (in instanceof CloseListenerRegistrationReply) {
+ if (in instanceof CloseDataChangeListenerRegistrationReply) {
return "match";
} else {
throw noMatch();
};
}
-}
\ No newline at end of file
+}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorRef;
+import akka.actor.Props;
import junit.framework.Assert;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-public class DistributedDataStoreTest {
+public class DistributedDataStoreTest extends AbstractActorTest{
private DistributedDataStore distributedDataStore;
+ private MockActorContext mockActorContext;
+ private ActorRef doNothingActorRef;
@org.junit.Before
public void setUp() throws Exception {
- distributedDataStore = new DistributedDataStore();
+ final Props props = Props.create(DoNothingActor.class);
+
+ doNothingActorRef = getSystem().actorOf(props);
+
+ mockActorContext = new MockActorContext(getSystem(), doNothingActorRef);
+ distributedDataStore = new DistributedDataStore(mockActorContext, "config");
+ distributedDataStore.onGlobalContextUpdated(
+ TestModel.createTestContext());
+
+ // Make CreateTransactionReply as the default response. Will need to be
+ // tuned if a specific test requires some other response
+ mockActorContext.setExecuteShardOperationResponse(
+ new CreateTransactionReply(doNothingActorRef.path()));
}
@org.junit.After
@org.junit.Test
public void testRegisterChangeListener() throws Exception {
+ mockActorContext.setExecuteShardOperationResponse(new RegisterChangeListenerReply(doNothingActorRef.path()));
ListenerRegistration registration =
- distributedDataStore.registerChangeListener(InstanceIdentifier.builder().build(), new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
+ distributedDataStore.registerChangeListener(TestModel.TEST_PATH, new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
@Override
public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
throw new UnsupportedOperationException("onDataChanged");
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
import scala.concurrent.duration.Duration;
}
@Test
- public void testOnReceiveFindPrimary() throws Exception {
+ public void testOnReceiveFindPrimaryForNonExistentShard() throws Exception {
new JavaTestKit(system) {{
- final Props props = Props.create(ShardManager.class);
- final TestActorRef<ShardManager> subject = TestActorRef.create(system, props, "test");
+ final Props props = ShardManager.props("config");
+ final TestActorRef<ShardManager> subject = TestActorRef.create(system, props);
- // can also use JavaTestKit “from the outside”
- final JavaTestKit probe = new JavaTestKit(system);
-
- // the run() method needs to finish within 3 seconds
- new Within(duration("3 seconds")) {
+ new Within(duration("1 seconds")) {
protected void run() {
subject.tell(new FindPrimary("inventory"), getRef());
};
}};
}
+
+ @Test
+ public void testOnReceiveFindPrimaryForExistentShard() throws Exception {
+
+ new JavaTestKit(system) {{
+ final Props props = ShardManager.props("config");
+ final TestActorRef<ShardManager> subject = TestActorRef.create(system, props);
+
+ // the run() method needs to finish within 3 seconds
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new FindPrimary(Shard.DEFAULT_NAME), getRef());
+
+ expectMsgClass(PrimaryFound.class);
+
+ expectNoMsg();
+ }
+ };
+ }};
+ }
}
\ No newline at end of file
@Test
public void testOnReceiveCreateTransactionChain() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = Props.create(Shard.class);
+ final Props props = Shard.props("config");
final ActorRef subject = getSystem().actorOf(props, "testCreateTransactionChain");
new Within(duration("1 seconds")) {
@Test
public void testOnReceiveRegisterListener() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = Props.create(Shard.class);
+ final Props props = Shard.props("config");
final ActorRef subject = getSystem().actorOf(props, "testRegisterChangeListener");
new Within(duration("1 seconds")) {
subject.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
- subject.tell(new RegisterChangeListener(InstanceIdentifier.builder().build(), noOpDataChangeListener() , AsyncDataBroker.DataChangeScope.BASE), getRef());
+ subject.tell(new RegisterChangeListener(TestModel.TEST_PATH, getRef().path() , AsyncDataBroker.DataChangeScope.BASE), getRef());
final String out = new ExpectMsg<String>("match hint") {
// do not put code outside this method, will run afterwards
}};
}
+
+
private AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> noOpDataChangeListener(){
return new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
@Override
}
};
}
-}
\ No newline at end of file
+}
import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
import org.opendaylight.controller.cluster.datastore.messages.WriteData;
import org.opendaylight.controller.cluster.datastore.messages.WriteDataReply;
+import org.opendaylight.controller.cluster.datastore.modification.CompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.DeleteModification;
+import org.opendaylight.controller.cluster.datastore.modification.MergeModification;
+import org.opendaylight.controller.cluster.datastore.modification.Modification;
+import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
public class ShardTransactionTest extends AbstractActorTest {
private static ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor());
@Test
public void testOnReceiveReadData() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef shard = getSystem().actorOf(Shard.props("config"));
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard);
final ActorRef subject = getSystem().actorOf(props, "testReadData");
new Within(duration("1 seconds")) {
}};
}
+ private void assertModification(final ActorRef subject, final Class<? extends Modification> modificationType){
+ new JavaTestKit(getSystem()) {{
+ new Within(duration("1 seconds")) {
+ protected void run() {
+ subject.tell(new ShardTransaction.GetCompositedModification(), getRef());
+
+ final CompositeModification compositeModification = new ExpectMsg<CompositeModification>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected CompositeModification match(Object in) {
+ if (in instanceof ShardTransaction.GetCompositeModificationReply) {
+ return ((ShardTransaction.GetCompositeModificationReply) in).getModification();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertTrue(compositeModification.getModifications().size() == 1);
+ assertEquals(modificationType, compositeModification.getModifications().get(0).getClass());
+
+ }
+ };
+ }};
+ }
+
@Test
public void testOnReceiveWriteData() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef shard = getSystem().actorOf(Shard.props("config"));
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard);
final ActorRef subject = getSystem().actorOf(props, "testWriteData");
new Within(duration("1 seconds")) {
assertEquals("match", out);
+ assertModification(subject, WriteModification.class);
expectNoMsg();
}
@Test
public void testOnReceiveMergeData() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef shard = getSystem().actorOf(Shard.props("config"));
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard);
final ActorRef subject = getSystem().actorOf(props, "testMergeData");
new Within(duration("1 seconds")) {
assertEquals("match", out);
+ assertModification(subject, MergeModification.class);
+
expectNoMsg();
}
@Test
public void testOnReceiveDeleteData() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef shard = getSystem().actorOf(Shard.props("config"));
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard);
final ActorRef subject = getSystem().actorOf(props, "testDeleteData");
new Within(duration("1 seconds")) {
assertEquals("match", out);
+ assertModification(subject, DeleteModification.class);
expectNoMsg();
}
@Test
public void testOnReceiveReadyTransaction() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef shard = getSystem().actorOf(Shard.props("config"));
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard);
final ActorRef subject = getSystem().actorOf(props, "testReadyTransaction");
new Within(duration("1 seconds")) {
@Test
public void testOnReceiveCloseTransaction() throws Exception {
new JavaTestKit(getSystem()) {{
- final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef shard = getSystem().actorOf(Shard.props("config"));
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard);
final ActorRef subject = getSystem().actorOf(props, "testCloseTransaction");
new Within(duration("1 seconds")) {
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+import java.util.List;
+
+public class TransactionProxyTest extends AbstractActorTest {
+
+ @Test
+ public void testRead() throws Exception {
+ final Props props = Props.create(DoNothingActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> read =
+ transactionProxy.read(TestModel.TEST_PATH);
+
+ Optional<NormalizedNode<?, ?>> normalizedNodeOptional = read.get();
+
+ Assert.assertFalse(normalizedNodeOptional.isPresent());
+
+ actorContext.setExecuteRemoteOperationResponse(new ReadDataReply(
+ ImmutableNodes.containerNode(TestModel.TEST_QNAME)));
+
+ read = transactionProxy.read(TestModel.TEST_PATH);
+
+ normalizedNodeOptional = read.get();
+
+ Assert.assertTrue(normalizedNodeOptional.isPresent());
+ }
+
+ @Test
+ public void testWrite() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.write(TestModel.TEST_PATH,
+ ImmutableNodes.containerNode(TestModel.NAME_QNAME));
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof WriteData);
+ }
+
+ @Test
+ public void testMerge() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.merge(TestModel.TEST_PATH,
+ ImmutableNodes.containerNode(TestModel.NAME_QNAME));
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof MergeData);
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.delete(TestModel.TEST_PATH);
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof DeleteData);
+ }
+
+ @Test
+ public void testReady() throws Exception {
+ final Props props = Props.create(DoNothingActor.class);
+ final ActorRef doNothingActorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(doNothingActorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse(new ReadyTransactionReply(doNothingActorRef.path()));
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+
+ DOMStoreThreePhaseCommitCohort ready = transactionProxy.ready();
+
+ Assert.assertTrue(ready instanceof ThreePhaseCommitCohortProxy);
+
+ ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready;
+
+ Assert.assertTrue("No cohort paths returned", proxy.getCohortPaths().size() > 0);
+
+ }
+
+ @Test
+ public void testGetIdentifier(){
+ final Props props = Props.create(DoNothingActor.class);
+ final ActorRef doNothingActorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(
+ new CreateTransactionReply(doNothingActorRef.path()));
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ Assert.assertNotNull(transactionProxy.getIdentifier());
+ }
+
+ @Test
+ public void testClose(){
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.close();
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof CloseTransaction);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Before;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public abstract class AbstractModificationTest {
+
+ protected InMemoryDOMDataStore store;
+
+ @Before
+ public void setUp(){
+ store = new InMemoryDOMDataStore("test", MoreExecutors.sameThreadExecutor());
+ store.onGlobalContextUpdated(TestModel.createTestContext());
+ }
+
+ protected void commitTransaction(DOMStoreWriteTransaction transaction){
+ DOMStoreThreePhaseCommitCohort cohort = transaction.ready();
+ cohort.preCommit();
+ cohort.commit();
+ }
+
+ protected Optional<NormalizedNode<?,?>> readData(InstanceIdentifier path) throws Exception{
+ DOMStoreReadTransaction transaction = store.newReadOnlyTransaction();
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> future = transaction.read(path);
+ return future.get();
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import com.google.common.base.Optional;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class DeleteModificationTest extends AbstractModificationTest{
+
+ @Test
+ public void testApply() throws Exception {
+ //Write something into the datastore
+ DOMStoreReadWriteTransaction writeTransaction = store.newReadWriteTransaction();
+ WriteModification writeModification = new WriteModification(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ writeModification.apply(writeTransaction);
+ commitTransaction(writeTransaction);
+
+ //Check if it's in the datastore
+ Optional<NormalizedNode<?,?>> data = readData(TestModel.TEST_PATH);
+ Assert.assertTrue(data.isPresent());
+
+ //Delete stuff from the datastore
+ DOMStoreWriteTransaction deleteTransaction = store.newWriteOnlyTransaction();
+ DeleteModification deleteModification = new DeleteModification(TestModel.TEST_PATH);
+ deleteModification.apply(deleteTransaction);
+ commitTransaction(deleteTransaction);
+
+ data = readData(TestModel.TEST_PATH);
+ Assert.assertFalse(data.isPresent());
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import com.google.common.base.Optional;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class MergeModificationTest extends AbstractModificationTest{
+
+ @Test
+ public void testApply() throws Exception {
+ //TODO : Need to write a better test for this
+
+ //Write something into the datastore
+ DOMStoreReadWriteTransaction writeTransaction = store.newReadWriteTransaction();
+ MergeModification writeModification = new MergeModification(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ writeModification.apply(writeTransaction);
+ commitTransaction(writeTransaction);
+
+ //Check if it's in the datastore
+ Optional<NormalizedNode<?,?>> data = readData(TestModel.TEST_PATH);
+ Assert.assertTrue(data.isPresent());
+
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import com.google.common.base.Optional;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class MutableCompositeModificationTest extends AbstractModificationTest {
+
+ @Test
+ public void testApply() throws Exception {
+
+ MutableCompositeModification compositeModification = new MutableCompositeModification();
+ compositeModification.addModification(new WriteModification(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)));
+
+ DOMStoreReadWriteTransaction transaction = store.newReadWriteTransaction();
+ compositeModification.apply(transaction);
+ commitTransaction(transaction);
+
+ Optional<NormalizedNode<?,?>> data = readData(TestModel.TEST_PATH);
+
+ Assert.assertNotNull(data.get());
+ Assert.assertEquals(TestModel.TEST_QNAME, data.get().getNodeType());
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.modification;
+
+import com.google.common.base.Optional;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class WriteModificationTest extends AbstractModificationTest{
+
+ @Test
+ public void testApply() throws Exception {
+ //Write something into the datastore
+ DOMStoreReadWriteTransaction writeTransaction = store.newReadWriteTransaction();
+ WriteModification writeModification = new WriteModification(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ writeModification.apply(writeTransaction);
+ commitTransaction(writeTransaction);
+
+ //Check if it's in the datastore
+ Optional<NormalizedNode<?,?>> data = readData(TestModel.TEST_PATH);
+ Assert.assertTrue(data.isPresent());
+
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import akka.actor.UntypedActor;
+
+public class DoNothingActor extends UntypedActor {
+
+ @Override public void onReceive(Object message) throws Exception {
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import akka.actor.UntypedActor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MessageCollectorActor collects messages as it receives them. It can send
+ * those collected messages to any sender which sends it the "messages" message
+ * <p>
+ * This class would be useful as a mock to test whether messages were sent
+ * to a remote actor or not.
+ * </p>
+ */
+public class MessageCollectorActor extends UntypedActor {
+ private List<Object> messages = new ArrayList<>();
+
+ @Override public void onReceive(Object message) throws Exception {
+ if(message instanceof String){
+ if("messages".equals(message)){
+ getSender().tell(messages, getSelf());
+ }
+ } else {
+ messages.add(message);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
+import scala.concurrent.duration.FiniteDuration;
+
+public class MockActorContext extends ActorContext {
+
+ private Object executeShardOperationResponse;
+ private Object executeRemoteOperationResponse;
+ private Object executeLocalOperationResponse;
+
+ public MockActorContext(ActorSystem actorSystem) {
+ super(actorSystem, null);
+ }
+
+ public MockActorContext(ActorSystem actorSystem, ActorRef shardManager) {
+ super(actorSystem, shardManager);
+ }
+
+
+ @Override public Object executeShardOperation(String shardName,
+ Object message, FiniteDuration duration) {
+ return executeShardOperationResponse;
+ }
+
+ @Override public Object executeRemoteOperation(ActorSelection actor,
+ Object message, FiniteDuration duration) {
+ return executeRemoteOperationResponse;
+ }
+
+ @Override public ActorSelection findPrimary(String shardName) {
+ return null;
+ }
+
+ public void setExecuteShardOperationResponse(Object response){
+ executeShardOperationResponse = response;
+ }
+
+ public void setExecuteRemoteOperationResponse(Object response){
+ executeRemoteOperationResponse = response;
+ }
+
+ public void setExecuteLocalOperationResponse(
+ Object executeLocalOperationResponse) {
+ this.executeLocalOperationResponse = executeLocalOperationResponse;
+ }
+
+
+}
--- /dev/null
+akka {
+ actor {
+ serializers {
+ java = "akka.serialization.JavaSerializer"
+ }
+
+ serialization-bindings {
+ "org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification" = java
+ }
+ }
+}
\ No newline at end of file
*
*
* @see DataProviderService
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.dom.api.DOMDataBroker}
*
*/
+@Deprecated
public interface DataBrokerService extends
BrokerService, //
DataReader<InstanceIdentifier, CompositeNode>, //
@Override
public CompositeNode readOperationalData(InstanceIdentifier path);
+ @Override
DataModificationTransaction beginTransaction();
}
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+/**
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener}
+ */
+@Deprecated
public interface DataChangeListener
extends
org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<InstanceIdentifier, CompositeNode> {
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-public interface DataModificationTransaction extends DataModification<InstanceIdentifier, CompositeNode>{
+/**
+ *
+ * @deprecated Replaced by more specific
+ * {@link org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction}
+ * ,
+ * {@link org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction}
+ * or {@link org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction}
+ *
+ */
+@Deprecated
+public interface DataModificationTransaction extends DataModification<InstanceIdentifier, CompositeNode> {
/**
* Returns transaction identifier
* Commits transaction to be stored in global data repository.
*
*
- * @return Future object which returns RpcResult with TransactionStatus
- * when transaction is processed by store.
+ * @return Future object which returns RpcResult with TransactionStatus when
+ * transaction is processed by store.
*/
@Override
Future<RpcResult<TransactionStatus>> commit();
ListenerRegistration<DataTransactionListener> registerListener(DataTransactionListener listener);
public interface DataTransactionListener extends EventListener {
- void onStatusUpdated(DataModificationTransaction transaction,TransactionStatus status);
+ void onStatusUpdated(DataModificationTransaction transaction, TransactionStatus status);
}
}
package org.opendaylight.controller.sal.core.api.data;
import org.opendaylight.controller.md.sal.common.api.data.DataProvisionService;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
import org.opendaylight.controller.sal.common.DataStoreIdentifier;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.controller.md.sal.common.api.data.DataReader;;
+/**
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.dom.api.DOMDataBroker}
+ *
+ */
+@Deprecated
public interface DataProviderService extends
DataBrokerService, //
DataProvisionService<InstanceIdentifier, CompositeNode>
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+/**
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore.
+ *
+ */
+@Deprecated
public interface DataStore extends //
DataReader<InstanceIdentifier, CompositeNode>,
DataCommitHandler<InstanceIdentifier, CompositeNode> {
* {@link DataProviderService#addValidator(DataStoreIdentifier, DataValidator)}
* </ul>
*
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.common.api.data.AsyncConfigurationCommitHandler}
+ *
**/
+@Deprecated
public interface DataValidator extends Provider.ProviderFunctionality {
/**
package org.opendaylight.controller.config.yang.md.sal.dom.impl;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
import org.opendaylight.controller.sal.core.api.data.DataStore;
-import org.opendaylight.controller.sal.dom.broker.BrokerConfigActivator;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
-import org.osgi.framework.BundleContext;
+import org.opendaylight.controller.sal.dom.broker.DataBrokerImpl;
+import org.opendaylight.controller.sal.dom.broker.GlobalBundleScanningSchemaServiceImpl;
+import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl;
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter;
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker;
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProviders;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
/**
*
public final class DomBrokerImplModule extends org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomBrokerImplModule
{
- private BundleContext bundleContext;
-
public DomBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
@Override
public java.lang.AutoCloseable createInstance() {
- final BrokerImpl broker = new BrokerImpl();
- final BrokerConfigActivator activator = new BrokerConfigActivator();
- final DataStore store = getDataStoreDependency();
+ final DataStore legacyStore = getDataStoreDependency();
final DOMDataBroker asyncBroker= getAsyncDataBrokerDependency();
- activator.start(broker, store, asyncBroker,getBundleContext());
+ ClassToInstanceMap<BrokerService> services = MutableClassToInstanceMap.create();
+
+
+ SchemaService schemaService = getSchemaServiceImpl();
+ services.putInstance(SchemaService.class, schemaService);
+ SchemaAwareRpcBroker router = new SchemaAwareRpcBroker("/", SchemaContextProviders
+ .fromSchemaService(schemaService));
+ services.putInstance(RpcProvisionRegistry.class, router);
+
+ final DataProviderService legacyData;
+ if(asyncBroker != null) {
+ services.putInstance(DOMDataBroker.class, asyncBroker);
+ legacyData = new BackwardsCompatibleDataBroker(asyncBroker,schemaService);
+ } else {
+ legacyData = createLegacyDataService(legacyStore,schemaService);
+ }
+ services.putInstance(DataProviderService.class,legacyData);
+ services.putInstance(DataBrokerService.class, legacyData);
-// final DomBrokerImplRuntimeMXBean domBrokerRuntimeMXBean = new DomBrokerRuntimeMXBeanImpl(activator.getDataService());
-// getRootRuntimeBeanRegistratorWrapper().register(domBrokerRuntimeMXBean);
- return broker;
+
+ MountPointManagerImpl mountService = new MountPointManagerImpl();
+ services.putInstance(MountService.class, mountService);
+ services.putInstance(MountProvisionService.class, mountService);
+
+ return new BrokerImpl(router, services);
}
- private BundleContext getBundleContext() {
- return this.bundleContext;
+ private DataProviderService createLegacyDataService(final DataStore legacyStore, final SchemaService schemaService) {
+ InstanceIdentifier rootPath = InstanceIdentifier.builder().toInstance();
+ DataBrokerImpl dataService = new DataBrokerImpl();
+ SchemaAwareDataStoreAdapter wrappedStore = new SchemaAwareDataStoreAdapter();
+ wrappedStore.changeDelegate(legacyStore);
+ wrappedStore.setValidationEnabled(false);
+
+ schemaService.registerSchemaServiceListener(wrappedStore);
+
+ dataService.registerConfigurationReader(rootPath, wrappedStore);
+ dataService.registerCommitHandler(rootPath, wrappedStore);
+ dataService.registerOperationalReader(rootPath, wrappedStore);
+ return dataService;
}
- public void setBundleContext(final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
+ private SchemaService getSchemaServiceImpl() {
+ final SchemaService schemaService;
+ if(getRootSchemaService() != null) {
+ schemaService = getRootSchemaServiceDependency();
+ } else {
+ schemaService = GlobalBundleScanningSchemaServiceImpl.getInstance();
+ }
+ return schemaService;
}
}
*/
package org.opendaylight.controller.config.yang.md.sal.dom.impl;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
/**
*
public class DomBrokerImplModuleFactory extends
org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomBrokerImplModuleFactory {
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
- DomBrokerImplModule module = (DomBrokerImplModule) super.createModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver,
- DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception {
- DomBrokerImplModule module = (DomBrokerImplModule) super.createModule(instanceName, dependencyResolver, old, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
}
*/
package org.opendaylight.controller.config.yang.md.sal.dom.impl;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.Executors;
+
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
-import org.osgi.framework.BundleContext;
-import java.util.Hashtable;
-import java.util.concurrent.Executors;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
/**
*
public final class DomInmemoryDataBrokerModule extends
org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractDomInmemoryDataBrokerModule {
- private BundleContext bundleContext;
-
public DomInmemoryDataBrokerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
- getBundleContext().registerService(DOMDataBroker.class, newDataBroker, new Hashtable<String, String>());
-
-
return newDataBroker;
}
-
- private BundleContext getBundleContext() {
- return bundleContext;
- }
-
- void setBundleContext(final BundleContext ctx) {
- bundleContext = ctx;
- }
}
*/
package org.opendaylight.controller.config.yang.md.sal.dom.impl;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
/**
*
{
-
- @Override
- public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
- DomInmemoryDataBrokerModule module = (DomInmemoryDataBrokerModule) super.createModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public Module createModule(final String instanceName, final DependencyResolver dependencyResolver,
- final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
- DomInmemoryDataBrokerModule module = (DomInmemoryDataBrokerModule) super.createModule(instanceName, dependencyResolver, old, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
}
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Override
public java.lang.AutoCloseable createInstance() {
- ServiceReference<SchemaService> ref = getBundleContext().getServiceReference(SchemaService.class);
- if (ref != null) {
- return new GlobalSchemaServiceProxy(getBundleContext(), ref);
- }
-
- GlobalBundleScanningSchemaServiceImpl newInstance = new GlobalBundleScanningSchemaServiceImpl(getBundleContext());
- newInstance.start();
- return newInstance;
+ return GlobalBundleScanningSchemaServiceImpl.getInstance();
}
public class GlobalSchemaServiceProxy implements AutoCloseable, SchemaService, Delegator<SchemaService> {
- private BundleContext bundleContext;
- private ServiceReference<SchemaService> reference;
private SchemaService delegate;
- public GlobalSchemaServiceProxy(final BundleContext bundleContext, final ServiceReference<SchemaService> ref) {
- this.bundleContext = bundleContext;
- this.reference = ref;
- this.delegate = bundleContext.getService(reference);
+ public GlobalSchemaServiceProxy() {
+ this.delegate = GlobalBundleScanningSchemaServiceImpl.getInstance();
}
@Override
public void close() throws Exception {
if (delegate != null) {
delegate = null;
-
- try {
- bundleContext.ungetService(reference);
- } catch (IllegalStateException e) {
- // Indicates the service was already unregistered which can happen normally
- // on shutdown.
- LOG.debug( "Error unregistering service", e );
- }
-
- reference = null;
- bundleContext = null;
}
}
package org.opendaylight.controller.md.sal.dom.broker.impl.compat;
+import javax.annotation.concurrent.ThreadSafe;
+
import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.core.api.data.DataProviderService;
import org.opendaylight.controller.sal.core.api.data.DataValidator;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
-public class BackwardsCompatibleDataBroker implements DataProviderService, SchemaContextListener {
+@ThreadSafe
+public class BackwardsCompatibleDataBroker implements DataProviderService {
private final DOMDataBroker backingBroker;
- private DataNormalizer normalizer;
+ private volatile DataNormalizer normalizer;
+ private final ListenerRegistration<SchemaServiceListener> schemaReg;
- public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl) {
+ public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl, final SchemaService schemaService) {
backingBroker = newBiDataImpl;
- }
-
- @Override
- public void onGlobalContextUpdated(final SchemaContext ctx) {
- normalizer = new DataNormalizer(ctx);
+ schemaReg = schemaService.registerSchemaServiceListener(new SchemaListener());
}
@Override
return listener;
}
}
+
+ private class SchemaListener implements SchemaContextListener {
+
+ @Override
+ public void onGlobalContextUpdated(final SchemaContext ctx) {
+ normalizer = new DataNormalizer(ctx);
+ }
+
+ }
}
import com.google.common.util.concurrent.ListenableFuture;
public abstract class BackwardsCompatibleTransaction<T extends DOMDataReadTransaction> implements
- DataModificationTransaction, Delegator<T> {
+DataModificationTransaction, Delegator<T> {
private static final Logger LOG = LoggerFactory.getLogger(BackwardsCompatibleTransaction.class);
LOG.trace("write {}:{} ",store,normalizedPath);
try {
- List<PathArgument> currentArguments = new ArrayList<>();
- DataNormalizationOperation<?> currentOp = getNormalizer().getRootOperation();
- Iterator<PathArgument> iterator = normalizedPath.getPath().iterator();
- while(iterator.hasNext()) {
- PathArgument currentArg = iterator.next();
- try {
- currentOp = currentOp.getChild(currentArg);
- } catch (DataNormalizationException e) {
- throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", normalizedPath), e);
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ while(iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", normalizedPath), e);
+ }
+ currentArguments.add(currentArg);
+ InstanceIdentifier currentPath = InstanceIdentifier.create(currentArguments);
+ boolean isPresent = getDelegate().read(store, currentPath).get().isPresent();
+ if(isPresent == false && iterator.hasNext()) {
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
}
- currentArguments.add(currentArg);
- InstanceIdentifier currentPath = new InstanceIdentifier(currentArguments);
- boolean isPresent = getDelegate().read(store, currentPath).get().isPresent();
- if(isPresent == false && iterator.hasNext()) {
- getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
- }
- }
} catch (InterruptedException | ExecutionException e) {
LOG.error("Exception durring read.",e);
}
+++ /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.dom.broker;
-
-import java.util.Hashtable;
-
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.controller.md.sal.dom.broker.impl.compat.BackwardsCompatibleDataBroker;
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.core.api.data.DataProviderService;
-import org.opendaylight.controller.sal.core.api.data.DataStore;
-import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountService;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProviders;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-
-public class BrokerConfigActivator implements AutoCloseable {
-
- private static InstanceIdentifier ROOT = InstanceIdentifier.builder()
- .toInstance();
-
- private DataProviderService dataService;
-
- private ServiceRegistration<DataBrokerService> dataReg = null;
- private ServiceRegistration<DataProviderService> dataProviderReg = null;
- private ServiceRegistration<MountService> mountReg = null;
- private ServiceRegistration<MountProvisionService> mountProviderReg = null;
- private SchemaService schemaService = null;
- private ServiceRegistration<RpcProvisionRegistry> rpcProvisionRegistryReg = null;
- private MountPointManagerImpl mountService = null;
-
- private SchemaAwareDataStoreAdapter wrappedStore = null;
-
- public void start(final BrokerImpl broker, final DataStore store,
- final DOMDataBroker asyncBroker, final BundleContext context) {
-
- final Hashtable<String, String> emptyProperties = new Hashtable<String, String>();
- broker.setBundleContext(context);
-
- final ServiceReference<SchemaService> serviceRef = context
- .getServiceReference(SchemaService.class);
- schemaService = context.<SchemaService> getService(serviceRef);
-
- broker.setRouter(new SchemaAwareRpcBroker("/", SchemaContextProviders
- .fromSchemaService(schemaService)));
-
- if (asyncBroker == null) {
- dataService = new DataBrokerImpl();
- dataProviderReg = context.registerService(
- DataProviderService.class, dataService, emptyProperties);
-
- wrappedStore = new SchemaAwareDataStoreAdapter();
- wrappedStore.changeDelegate(store);
- wrappedStore.setValidationEnabled(false);
- context.registerService(SchemaServiceListener.class, wrappedStore,
- emptyProperties);
-
- dataService.registerConfigurationReader(ROOT, wrappedStore);
- dataService.registerCommitHandler(ROOT, wrappedStore);
- dataService.registerOperationalReader(ROOT, wrappedStore);
- } else {
- BackwardsCompatibleDataBroker compatibleDataBroker = new BackwardsCompatibleDataBroker(
- asyncBroker);
- context.registerService(SchemaServiceListener.class,
- compatibleDataBroker, emptyProperties);
- dataService = compatibleDataBroker;
- }
-
- mountService = new MountPointManagerImpl();
- dataReg = context.registerService(DataBrokerService.class, dataService,
- emptyProperties);
- mountReg = context.registerService(MountService.class, mountService,
- emptyProperties);
- mountProviderReg = context.registerService(MountProvisionService.class,
- mountService, emptyProperties);
-
- rpcProvisionRegistryReg = context
- .registerService(RpcProvisionRegistry.class,
- broker.getRouter(), emptyProperties);
- }
-
- @Override
- public void close() {
-
- if (dataReg != null) {
- dataReg.unregister();
- dataReg = null;
- }
- if (dataProviderReg != null) {
- dataProviderReg.unregister();
- dataProviderReg = null;
- }
- if (mountReg != null) {
- mountReg.unregister();
- mountReg = null;
- }
- if (mountProviderReg != null) {
- mountProviderReg.unregister();
- mountProviderReg = null;
- }
- if (rpcProvisionRegistryReg != null) {
- rpcProvisionRegistryReg.unregister();
- rpcProvisionRegistryReg = null;
- }
- }
-
- /**
- * @return the dataService
- */
- public DataProviderService getDataService() {
- return dataService;
- }
-
- /**
- * @param dataService
- * the dataService to set
- */
- public void setDataService(final DataProviderService dataService) {
- this.dataService = dataService;
- }
-}
*/
package org.opendaylight.controller.sal.dom.broker;
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.ListenableFuture;
-
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
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.BrokerService;
import org.opendaylight.controller.sal.core.api.Consumer;
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.controller.sal.dom.broker.spi.RpcRouter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
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.Logger;
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.sal.core.api.RpcRoutingContext;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.util.concurrent.ListenableFuture;
public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable {
private final static Logger log = LoggerFactory.getLogger(BrokerImpl.class);
private final Set<ProviderContextImpl> providerSessions = Collections
.synchronizedSet(new HashSet<ProviderContextImpl>());
- private BundleContext bundleContext = null;
-
private AutoCloseable deactivator = null;
private RpcRouter router = null;
+ private final ClassToInstanceMap<BrokerService> services;
+
+ public BrokerImpl(final RpcRouter router,final ClassToInstanceMap<BrokerService> services) {
+ this.router = Preconditions.checkNotNull(router, "RPC Router must not be null");
+ this.services = ImmutableClassToInstanceMap.copyOf(services);
+ }
+
+
@Override
public ConsumerSession registerConsumer(final Consumer consumer,
final BundleContext ctx) {
private void checkPredicates(final Provider prov) {
Preconditions.checkNotNull(prov, "Provider should not be null.");
for (ProviderContextImpl session : providerSessions) {
- if (prov.equals(session.getProvider()))
+ if (prov.equals(session.getProvider())) {
throw new IllegalStateException("Provider already registered");
+ }
}
}
private void checkPredicates(final Consumer cons) {
Preconditions.checkNotNull(cons, "Consumer should not be null.");
for (ConsumerContextImpl session : sessions) {
- if (cons.equals(session.getConsumer()))
+ if (cons.equals(session.getConsumer())) {
throw new IllegalStateException("Consumer already registered");
+ }
}
}
// Private Factory methods
private ConsumerContextImpl newSessionFor(final Consumer provider,
final BundleContext ctx) {
- ConsumerContextImpl ret = new ConsumerContextImpl(provider, ctx);
- ret.setBroker(this);
+ ConsumerContextImpl ret = new ConsumerContextImpl(provider, this);
return ret;
}
private ProviderContextImpl newSessionFor(final Provider provider,
final BundleContext ctx) {
- ProviderContextImpl ret = new ProviderContextImpl(provider, ctx);
- ret.setBroker(this);
+ ProviderContextImpl ret = new ProviderContextImpl(provider, this);
return ret;
}
return router.invokeRpc(rpc, input);
}
- /**
- * @return the bundleContext
- */
- public BundleContext getBundleContext() {
- return bundleContext;
- }
-
- /**
- * @param bundleContext
- * the bundleContext to set
- */
- public void setBundleContext(final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
/**
* @return the deactivator
*/
public void setRouter(final RpcRouter router) {
this.router = router;
}
+
+ protected <T extends BrokerService> Optional<T> getGlobalService(final Class<T> service) {
+ return Optional.fromNullable(services.getInstance(service));
+ }
+
}
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;
private final ClassToInstanceMap<BrokerService> instantiatedServices = MutableClassToInstanceMap
.create();
- private final BundleContext context;
private final Consumer consumer;
private BrokerImpl broker = null;
@GuardedBy("this")
private boolean closed = false;
- public ConsumerContextImpl(final Consumer consumer, final BundleContext ctx) {
- this.consumer = consumer;
- this.context = ctx;
+ public ConsumerContextImpl(final Consumer provider, final BrokerImpl brokerImpl) {
+ broker = brokerImpl;
+ consumer = provider;
}
@Override
public Future<RpcResult<CompositeNode>> rpc(final QName rpc,
final CompositeNode input) {
+ checkNotClosed();
return broker.invokeRpcAsync(rpc, input);
}
@Override
public <T extends BrokerService> T getService(final Class<T> service) {
+ checkNotClosed();
final T localProxy = instantiatedServices.getInstance(service);
if (localProxy != null) {
return localProxy;
}
- final ServiceReference<T> serviceRef = context
- .getServiceReference(service);
- if (serviceRef == null) {
- return null;
- }
- final T serviceImpl = context.getService(serviceRef);
- final T ret = ProxyFactory.createProxy(serviceRef, serviceImpl);
- if (ret != null) {
+ final Optional<T> serviceImpl = broker.getGlobalService(service);
+ if(serviceImpl.isPresent()) {
+ final T ret = ProxyFactory.createProxy(null,serviceImpl.get());
instantiatedServices.putInstance(service, ret);
+ return ret;
+ } else {
+ return null;
}
- return ret;
}
@Override
}
}
broker.consumerSessionClosed(this);
+ broker = null;
}
@Override
/**
* @return the broker
*/
- public BrokerImpl getBroker() {
+ protected final BrokerImpl getBrokerChecked() {
+ checkNotClosed();
return broker;
}
- /**
- * @param broker
- * the broker to set
- */
- public void setBroker(final BrokerImpl broker) {
- this.broker = broker;
- }
-
/**
* @return the _consumer
*/
public Consumer getConsumer() {
return consumer;
}
+
+ protected final void checkNotClosed() {
+ Preconditions.checkState(!closed, "Session is closed.");
+ }
}
import static com.google.common.base.Preconditions.checkState;
+import com.google.common.annotations.VisibleForTesting;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
private ServiceTracker<SchemaServiceListener, SchemaServiceListener> listenerTracker;
private BundleTracker<Iterable<Registration<URL>>> bundleTracker;
private boolean starting = true;
+ private static GlobalBundleScanningSchemaServiceImpl instance;
- public GlobalBundleScanningSchemaServiceImpl(final BundleContext context) {
+ private GlobalBundleScanningSchemaServiceImpl(final BundleContext context) {
this.context = Preconditions.checkNotNull(context);
}
+ public synchronized static GlobalBundleScanningSchemaServiceImpl createInstance(final BundleContext ctx) {
+ Preconditions.checkState(instance == null);
+ instance = new GlobalBundleScanningSchemaServiceImpl(ctx);
+ instance.start();
+ return instance;
+ }
+
+ public synchronized static GlobalBundleScanningSchemaServiceImpl getInstance() {
+ Preconditions.checkState(instance != null, "Global Instance was not instantiated");
+ return instance;
+ }
+
+ @VisibleForTesting
+ public static synchronized void destroyInstance() {
+ instance = null;
+ }
+
public BundleContext getContext() {
return context;
}
}
@Override
- public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+ public synchronized ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
if(potentialCtx.isPresent()) {
listener.onGlobalContextUpdated(potentialCtx.get());
}
- private void updateContext(final SchemaContext snapshot) {
+ private synchronized void updateContext(final SchemaContext snapshot) {
Object[] services = listenerTracker.getServices();
for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
try {
}
@Override
- public SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
+ public synchronized SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
SchemaServiceListener listener = context.getService(reference);
SchemaContext _ctxContext = getGlobalContext();
private SchemaContext schemaContext;
- public MountPointImpl(InstanceIdentifier path) {
+ public MountPointImpl(final InstanceIdentifier path) {
this.mountPath = path;
rpcs = new SchemaAwareRpcBroker(path.toString(),this);
dataReader = new DataBrokerImpl();
}
@Override
- public void publish(CompositeNode notification) {
+ public void publish(final CompositeNode notification) {
notificationRouter.publish(notification);
}
@Override
- public Registration<NotificationListener> addNotificationListener(QName notification, NotificationListener listener) {
+ public Registration<NotificationListener> addNotificationListener(final QName notification, final NotificationListener listener) {
return notificationRouter.addNotificationListener(notification, listener);
}
@Override
- public CompositeNode readConfigurationData(InstanceIdentifier path) {
+ public CompositeNode readConfigurationData(final InstanceIdentifier path) {
return dataReader.readConfigurationData(path);
}
@Override
- public CompositeNode readOperationalData(InstanceIdentifier path) {
+ public CompositeNode readOperationalData(final InstanceIdentifier path) {
return dataReader.readOperationalData(path);
}
@Override
public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerOperationalReader(
- InstanceIdentifier path, DataReader<InstanceIdentifier, CompositeNode> reader) {
+ final InstanceIdentifier path, final DataReader<InstanceIdentifier, CompositeNode> reader) {
return dataReader.registerOperationalReader(path, reader);
}
@Override
public Registration<DataReader<InstanceIdentifier, CompositeNode>> registerConfigurationReader(
- InstanceIdentifier path, DataReader<InstanceIdentifier, CompositeNode> reader) {
+ final InstanceIdentifier path, final DataReader<InstanceIdentifier, CompositeNode> reader) {
return dataReader.registerConfigurationReader(path, reader);
}
@Override
- public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+ public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
return rpcs.addRoutedRpcImplementation(rpcType, implementation);
}
@Override
- public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
- rpcs.setRoutedRpcDefaultDelegate(defaultImplementation);
+ public void setRoutedRpcDefaultDelegate(final RoutedRpcDefaultImplementation defaultImplementation) {
+ rpcs.setRoutedRpcDefaultDelegate(defaultImplementation);
}
- @Override
- public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+ @Override
+ public RpcRegistration addRpcImplementation(final QName rpcType, final RpcImplementation implementation)
throws IllegalArgumentException {
return rpcs.addRpcImplementation(rpcType, implementation);
}
}
@Override
- public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
+ public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
return rpcs.invokeRpc(rpc, input);
}
@Override
- public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener) {
+ public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(final RpcRegistrationListener listener) {
return rpcs.addRpcRegistrationListener(listener);
}
@Override
- public ListenableFuture<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input) {
+ public ListenableFuture<RpcResult<CompositeNode>> rpc(final QName type, final CompositeNode input) {
return rpcs.invokeRpc( type, input );
}
}
@Override
- public ListenerRegistration<DataChangeListener> registerDataChangeListener(InstanceIdentifier path,
- DataChangeListener listener) {
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(final InstanceIdentifier path,
+ final DataChangeListener listener) {
return dataReader.registerDataChangeListener(path, listener);
}
@Override
public Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> registerCommitHandler(
- InstanceIdentifier path, DataCommitHandler<InstanceIdentifier, CompositeNode> commitHandler) {
+ final InstanceIdentifier path, final DataCommitHandler<InstanceIdentifier, CompositeNode> commitHandler) {
return dataReader.registerCommitHandler(path, commitHandler);
}
@Override
- public void removeRefresher(DataStoreIdentifier store, DataRefresher refresher) {
- // NOOP
+ public void removeRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
+ // NOOP
}
@Override
- public void addRefresher(DataStoreIdentifier store, DataRefresher refresher) {
- // NOOP
+ public void addRefresher(final DataStoreIdentifier store, final DataRefresher refresher) {
+ // NOOP
}
@Override
- public void addValidator(DataStoreIdentifier store, DataValidator validator) {
- // NOOP
+ public void addValidator(final DataStoreIdentifier store, final DataValidator validator) {
+ // NOOP
}
@Override
- public void removeValidator(DataStoreIdentifier store, DataValidator validator) {
+ public void removeValidator(final DataStoreIdentifier store, final DataValidator validator) {
// NOOP
}
}
@Override
- public void setSchemaContext(SchemaContext schemaContext) {
+ public void setSchemaContext(final SchemaContext schemaContext) {
this.schemaContext = schemaContext;
}
class ReadWrapper implements DataReader<InstanceIdentifier, CompositeNode> {
-
-
- private InstanceIdentifier shortenPath(InstanceIdentifier path) {
+ private InstanceIdentifier shortenPath(final InstanceIdentifier path) {
InstanceIdentifier ret = null;
if(mountPath.contains(path)) {
List<PathArgument> newArgs = path.getPath().subList(mountPath.getPath().size(), path.getPath().size());
- ret = new InstanceIdentifier(newArgs);
+ ret = InstanceIdentifier.create(newArgs);
}
return ret;
}
@Override
- public CompositeNode readConfigurationData(InstanceIdentifier path) {
+ public CompositeNode readConfigurationData(final InstanceIdentifier path) {
InstanceIdentifier newPath = shortenPath(path);
if(newPath == null) {
return null;
}
@Override
- public CompositeNode readOperationalData(InstanceIdentifier path) {
+ public CompositeNode readOperationalData(final InstanceIdentifier path) {
InstanceIdentifier newPath = shortenPath(path);
if(newPath == null) {
return null;
@Override
public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>>> registerCommitHandlerListener(
- RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>> commitHandlerListener) {
+ final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>> commitHandlerListener) {
return dataReader.registerCommitHandlerListener(commitHandlerListener);
}
@Override
public <L extends RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> ListenerRegistration<L> registerRouteChangeListener(
- L listener) {
+ final L listener) {
return rpcs.registerRouteChangeListener(listener);
}
}
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
-import org.osgi.framework.BundleContext;
class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession {
private final Set<RpcRegistrationWrapper> registrations = new HashSet<>();
private final Provider provider;
- public ProviderContextImpl(final Provider provider, final BundleContext ctx) {
- super(null, ctx);
+ public ProviderContextImpl(final Provider provider, final BrokerImpl broker) {
+ super(null, broker);
this.provider = provider;
}
@Override
public RpcRegistrationWrapper addRpcImplementation(final QName rpcType,
final RpcImplementation implementation) throws IllegalArgumentException {
- final RpcRegistration origReg = getBroker().getRouter()
+ final RpcRegistration origReg = getBrokerChecked().getRouter()
.addRpcImplementation(rpcType, implementation);
final RpcRegistrationWrapper newReg = new RpcRegistrationWrapper(
origReg);
final QName rpcType, final RpcImplementation implementation) {
throw new UnsupportedOperationException(
"TODO: auto-generated method stub");
+
}
@Override
public RoutedRpcRegistration addRoutedRpcImplementation(
final QName rpcType, final RpcImplementation implementation) {
- throw new UnsupportedOperationException(
- "TODO: auto-generated method stub");
+ return getBrokerChecked().getRouter().addRoutedRpcImplementation(rpcType, implementation);
}
@Override
public Set<QName> getSupportedRpcs() {
- return getBroker().getRouter().getSupportedRpcs();
+ return getBrokerChecked().getRouter().getSupportedRpcs();
}
@Override
public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(
final RpcRegistrationListener listener) {
- return getBroker().getRouter().addRpcRegistrationListener(listener);
+ return getBrokerChecked().getRouter().addRpcRegistrationListener(listener);
}
/**
import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.Iterables;
+
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.Iterables;
-
public class DataReaderRouter extends
AbstractDataReadRouter<InstanceIdentifier, CompositeNode> {
private final static Logger LOG = LoggerFactory
@Override
protected CompositeNodeTOImpl merge(final InstanceIdentifier path,
final Iterable<CompositeNode> data) {
- PathArgument pathArgument = Iterables.getLast(path.getPath(), null);
+ PathArgument pathArgument = Iterables.getLast(path.getPathArguments(), null);
boolean empty = true;
QName name = (pathArgument == null ? null : pathArgument.getNodeType());
final ArrayList<Node<?>> nodes = new ArrayList<Node<?>>();
import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-
public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
DataStore, //
SchemaContextListener, //
public CompositeNode readConfigurationData(final InstanceIdentifier path) {
getDelegateReadLock().lock();
try {
- if (path.getPath().isEmpty()) {
+ if (Iterables.isEmpty(path.getPathArguments())) {
return null;
}
QName qname = null;
public CompositeNode readOperationalData(final InstanceIdentifier path) {
getDelegateReadLock().lock();
try {
- if (path.getPath().isEmpty()) {
+ if (Iterables.isEmpty(path.getPathArguments())) {
return null;
}
QName qname = null;
import java.util.HashSet;
import java.util.Set;
+import javax.annotation.Nullable;
+
import org.opendaylight.controller.sal.core.api.BrokerService;
import org.opendaylight.yangtools.concepts.Registration;
import org.osgi.framework.ServiceReference;
private T delegate;
private final ServiceReference<T> reference;
- public AbstractBrokerServiceProxy(final ServiceReference<T> ref, final T delegate) {
+ public AbstractBrokerServiceProxy(final @Nullable ServiceReference<T> ref, final T delegate) {
this.delegate = checkNotNull(delegate, "Delegate should not be null.");
- this.reference = checkNotNull(ref, "Reference should not be null.");
+ this.reference = ref;
}
protected final T getDelegate() {
@Override
public void start(final BundleContext context) {
- schemaService = new GlobalBundleScanningSchemaServiceImpl(context);
+ schemaService = GlobalBundleScanningSchemaServiceImpl.createInstance(context);
schemaService.start();
schemaServiceReg = context.registerService(SchemaService.class, schemaService, new Hashtable<String,String>());
}
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
public class YangDataOperations {
public static CompositeNode merge(final DataSchemaNode schema,
}
@SuppressWarnings({ "unchecked", "rawtypes" })
final Map<Map<QName, Object>, CompositeNode> originalMap = YangDataUtils
- .toIndexMap((List) original, node.getKeyDefinition());
+ .toIndexMap((List) original, node.getKeyDefinition());
@SuppressWarnings({ "unchecked", "rawtypes" })
final Map<Map<QName, Object>, CompositeNode> modifiedMap = YangDataUtils
- .toIndexMap((List) modified, node.getKeyDefinition());
+ .toIndexMap((List) modified, node.getKeyDefinition());
final List<Node<?>> mergedNodes = new ArrayList<Node<?>>(
original.size() + modified.size());
modified.getNodeType()));
final List<Node<?>> mergedChildNodes = new ArrayList<Node<?>>(stored
- .getChildren().size() + modified.getChildren().size());
+ .getValue().size() + modified.getValue().size());
final Set<QName> toProcess = new HashSet<QName>(stored.keySet());
toProcess.addAll(modified.keySet());
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
import org.opendaylight.yangtools.yang.model.api.UsesNode;
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-
public final class YangSchemaUtils {
private static final Function<PathArgument, QName> QNAME_FROM_PATH_ARGUMENT = new Function<PathArgument, QName>(){
public static DataSchemaNode getSchemaNode(final SchemaContext schema,final InstanceIdentifier path) {
checkArgument(schema != null,"YANG Schema must not be null.");
checkArgument(path != null,"Path must not be null.");
- return getSchemaNode(schema, FluentIterable.from(path.getPath()).transform(QNAME_FROM_PATH_ARGUMENT));
+ return getSchemaNode(schema, FluentIterable.from(path.getPathArguments()).transform(QNAME_FROM_PATH_ARGUMENT));
}
public static DataSchemaNode getSchemaNode(final SchemaContext schema,final Iterable<QName> path) {
}
}
}
-
+
container async-data-broker {
uses config:service-ref {
refine type {
config:required-identity sal:dom-async-data-broker;
}
}
-
+ }
+
+ container root-schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity sal:schema-service;
+ }
+ }
}
}
}
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.annotation.concurrent.GuardedBy;
+
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.concurrent.GuardedBy;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
/**
* A set of listeners organized as a tree by node to which they listen. This class
* allows for efficient lookup of listeners when we walk the DataTreeCandidate.
try {
Node walkNode = rootNode;
- for (final PathArgument arg : path.getPath()) {
+ for (final PathArgument arg : path.getPathArguments()) {
walkNode = walkNode.ensureChild(arg);
}
import java.io.File;
import java.io.InputStream;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders;
import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-import com.google.common.net.InetAddresses;
-import io.netty.util.HashedWheelTimer;
-
/**
*
*/
@Override
protected void customValidation() {
checkNotNull(getAddress(), addressJmxAttribute);
+ checkCondition(isHostAddressPresent(getAddress()), "Host address not present in " + getAddress(), addressJmxAttribute);
checkNotNull(getPort(), portJmxAttribute);
checkNotNull(getDomRegistry(), portJmxAttribute);
checkNotNull(getDomRegistry(), domRegistryJmxAttribute);
checkNotNull(getBetweenAttemptsTimeoutMillis(), betweenAttemptsTimeoutMillisJmxAttribute);
checkCondition(getBetweenAttemptsTimeoutMillis() > 0, "must be > 0", betweenAttemptsTimeoutMillisJmxAttribute);
- // FIXME BUG-944 remove backwards compatibility
- if(getClientDispatcher() == null) {
- checkCondition(getBossThreadGroup() != null, "Client dispatcher was not set, thread groups have to be set instead", bossThreadGroupJmxAttribute);
- checkCondition(getWorkerThreadGroup() != null, "Client dispatcher was not set, thread groups have to be set instead", workerThreadGroupJmxAttribute);
- }
+ checkNotNull(getClientDispatcher(), clientDispatcherJmxAttribute);
+ checkNotNull(getBindingRegistry(), bindingRegistryJmxAttribute);
+ checkNotNull(getProcessingExecutor(), processingExecutorJmxAttribute);
// Check username + password in case of ssh
if(getTcpOnly() == false) {
checkNotNull(getPassword(), passwordJmxAttribute);
}
- // FIXME BUG 944 remove this warning
- if(getBindingRegistry() == null) {
- logger.warn("Configuration property: \"binding-registry\" not set for sal-netconf-connector (" + getIdentifier() + "). " +
- "Netconf-connector now requires a dependency on \"binding-broker-osgi-registry\". " +
- "The dependency is optional for now to preserve backwards compatibility, but will be mandatory in the future. " +
- "Please set the property as in \"01-netconf-connector\" initial config file. " +
- "The service will be retrieved from OSGi service registry now.");
- }
+ }
- // FIXME BUG 944 remove this warning
- if(getProcessingExecutor() == null) {
- logger.warn("Configuration property: \"processing-executor\" not set for sal-netconf-connector (" + getIdentifier() + "). " +
- "Netconf-connector now requires a dependency on \"threadpool\". " +
- "The dependency is optional for now to preserve backwards compatibility, but will be mandatory in the future. " +
- "Please set the property as in \"01-netconf-connector\" initial config file. " +
- "New instance will be created for the executor.");
- }
+ private boolean isHostAddressPresent(Host address) {
+ return address.getDomainName() != null ||
+ address.getIpAddress() != null && (address.getIpAddress().getIpv4Address() != null || address.getIpAddress().getIpv6Address() != null);
}
@Override
public java.lang.AutoCloseable createInstance() {
final RemoteDeviceId id = new RemoteDeviceId(getIdentifier());
- final ExecutorService globalProcessingExecutor = getGlobalProcessingExecutor();
+ final ExecutorService globalProcessingExecutor = getProcessingExecutorDependency().getExecutor();
final Broker domBroker = getDomRegistryDependency();
- final BindingAwareBroker bindingBroker = getBindingRegistryBackwards();
+ final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
final NetconfDevice device =
final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(id, device);
final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
- // FIXME BUG-944 remove backwards compatibility
- final NetconfClientDispatcher dispatcher = getClientDispatcher() == null ? createDispatcher() : getClientDispatcherDependency();
+ final NetconfClientDispatcher dispatcher = getClientDispatcherDependency();
listener.initializeRemoteConnection(dispatcher, clientConfig);
return new AutoCloseable() {
};
}
- private BindingAwareBroker getBindingRegistryBackwards() {
- if(getBindingRegistry() != null) {
- return getBindingRegistryDependency();
- } else {
- // FIXME BUG 944 remove backwards compatibility
- final ServiceReference<BindingAwareBroker> serviceReference = bundleContext.getServiceReference(BindingAwareBroker.class);
- Preconditions
- .checkNotNull(
- serviceReference,
- "Unable to retrieve %s from OSGi service registry, use binding-registry config property to inject %s with config subsystem",
- BindingAwareBroker.class, BindingAwareBroker.class);
- return bundleContext.getService(serviceReference);
- }
- }
-
- private ExecutorService getGlobalProcessingExecutor() {
- if(getProcessingExecutor() != null) {
- return getProcessingExecutorDependency().getExecutor();
- } else {
- // FIXME BUG 944 remove backwards compatibility
- return Executors.newCachedThreadPool();
- }
- }
-
private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider() {
if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
final String storageFile = "cache/schema";
return GLOBAL_NETCONF_SOURCE_PROVIDER;
}
- // FIXME BUG-944 remove backwards compatibility
- /**
- * @deprecated Use getClientDispatcherDependency method instead to retrieve injected dispatcher.
- * This one creates new instance of NetconfClientDispatcher and will be removed in near future.
- */
- @Deprecated
- private NetconfClientDispatcher createDispatcher() {
- return new NetconfClientDispatcherImpl(getBossThreadGroupDependency(), getWorkerThreadGroupDependency(), new HashedWheelTimer());
- }
-
public void setBundleContext(final BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
}
private InetSocketAddress getSocketAddress() {
- /*
- * Uncomment after Switch to IP Address
- if(getAddress().getIpv4Address() != null) {
- addressValue = getAddress().getIpv4Address().getValue();
+ if(getAddress().getDomainName() != null) {
+ return new InetSocketAddress(getAddress().getDomainName().getValue(), getPort().getValue());
} else {
- addressValue = getAddress().getIpv6Address().getValue();
+ IpAddress ipAddress = getAddress().getIpAddress();
+ String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
+ return new InetSocketAddress(ip, getPort().getValue());
}
- */
- final InetAddress inetAddress = InetAddresses.forString(getAddress());
- return new InetSocketAddress(inetAddress, getPort().intValue());
}
}
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.NetconfClientSession;
import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
}
public void initializeRemoteConnection(final NetconfClientDispatcher dispatch,
- final NetconfReconnectingClientConfiguration config) {
- dispatch.createReconnectingClient(config);
+ final NetconfClientConfiguration config) {
+ if(config instanceof NetconfReconnectingClientConfiguration) {
+ dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
+ }
+
+ dispatch.createClient(config);
}
private void tearDown( String reason ) {
Thread.currentThread().interrupt();
throw new RuntimeException(id + ": Interrupted while waiting for response", e);
} catch (final ExecutionException e) {
- logger.warn("%s: Error executing pre commit operation on remote device", id, e);
+ logger.warn("{}: Error executing pre commit operation on remote device", id, e);
return new FailingTransaction(twoPhaseCommit, e);
}
private static Node<?> findNode(final CompositeNode node, final InstanceIdentifier identifier) {
Node<?> current = node;
- for (final InstanceIdentifier.PathArgument arg : identifier.getPath()) {
+ for (final InstanceIdentifier.PathArgument arg : identifier.getPathArguments()) {
if (current instanceof SimpleNode<?>) {
return null;
} else if (current instanceof CompositeNode) {
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
/**
* Remote transaction that delegates data change to remote device using netconf messages.
*/
}
private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException {
- sendEditRpc(createEditConfigStructure(key, Optional.<String>absent(), Optional.of(value)));
+ sendEditRpc(createEditConfigStructure(key, Optional.<String>absent(), Optional.of(value)), Optional.<String>absent());
}
private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException {
- sendEditRpc(createEditConfigStructure(toDelete, Optional.of("delete"), Optional.<CompositeNode>absent()));
+ // FIXME use org.opendaylight.yangtools.yang.data.api.ModifyAction instead of strings
+ // TODO add string lowercase value to ModifyAction enum entries
+ sendEditRpc(createEditConfigStructure(toDelete, Optional.of("delete"), Optional.<CompositeNode>absent()), Optional.of("none"));
}
- private void sendEditRpc(final CompositeNode editStructure) throws InterruptedException, ExecutionException {
- final ImmutableCompositeNode editConfigRequest = createEditConfigRequest(editStructure);
+ private void sendEditRpc(final CompositeNode editStructure, final Optional<String> defaultOperation) throws InterruptedException, ExecutionException {
+ final ImmutableCompositeNode editConfigRequest = createEditConfigRequest(editStructure, defaultOperation);
final RpcResult<CompositeNode> rpcResult = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, editConfigRequest).get();
- // TODO 874 add default operation when sending delete
// Check result
if(rpcResult.isSuccessful() == false) {
}
}
- private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure) {
+ private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure, final Optional<String> defaultOperation) {
final CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
+ // Target
final Node<?> targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.<Node<?>>of(targetNode));
ret.add(targetWrapperNode);
+ // Default operation
+ if(defaultOperation.isPresent()) {
+ SimpleNode<String> defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, defaultOperation.get());
+ ret.add(defOp);
+ }
+
+ // Error option
if(rollbackSupported) {
ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION);
}
+
ret.setQName(NETCONF_EDIT_CONFIG_QNAME);
+ // Edit content
ret.add(editStructure);
return ret.toInstance();
}
private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional<String> operation,
- final Optional<CompositeNode> lastChildOverride) {
- Preconditions.checkArgument(dataPath.getPath().isEmpty() == false, "Instance identifier with empty path %s", dataPath);
+ final Optional<CompositeNode> lastChildOverride) {
+ Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
List<PathArgument> reversedPath = Lists.reverse(dataPath.getPath());
final DataNodeContainer schemaForEdit = NetconfMessageTransformUtil.createSchemaForEdit(schemaContext.get());
w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaForEdit, codecProvider);
} else {
+ // FIXME get and get-config needs schema as well to transform filter using schema context
+ // e.g. Identityref nodes in filter fail to serialize properly to xml without schema
w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, schemaContext.get(), codecProvider);
}
} else {
*/
package org.opendaylight.controller.sal.connect.netconf.util;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
public class NetconfMessageTransformUtil {
private NetconfMessageTransformUtil() {
}
- public static final QName IETF_NETCONF_MONITORING = QName.create(
- "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
+ public static final QName IETF_NETCONF_MONITORING = QName.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option");
public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
- static List<Node<?>> RUNNING = Collections.<Node<?>> singletonList(new SimpleNodeTOImpl<>(NETCONF_RUNNING_QNAME,
- null, null));
+ static List<Node<?>> RUNNING = Collections.<Node<?>> singletonList(new SimpleNodeTOImpl<>(NETCONF_RUNNING_QNAME, null, null));
public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
public static CompositeNode CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
public static QName NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
public static QName NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
public static QName NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation");
+ public static QName NETCONF_DEFAULT_OPERATION_QNAME = QName.create(NETCONF_OPERATION_QNAME, "default-operation");
public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
public static Node<?> toFilterStructure(final InstanceIdentifier identifier) {
Node<?> previous = null;
- if (identifier.getPath().isEmpty()) {
+ if (Iterables.isEmpty(identifier.getPathArguments())) {
return null;
}
}
public static void checkValidReply(final NetconfMessage input, final NetconfMessage output)
- throws NetconfDocumentedException {
+ throws NetconfDocumentedException {
final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
if(inputMsgId.equals(outputMsgId) == false) {
Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
- .put( "actual-message-id", outputMsgId )
- .put( "expected-message-id", inputMsgId )
- .build();
+ .put( "actual-message-id", outputMsgId )
+ .put( "expected-message-id", inputMsgId )
+ .build();
throw new NetconfDocumentedException( "Response message contained unknown \"message-id\"",
null, NetconfDocumentedException.ErrorType.protocol,
}
}
- public static RpcError toRpcError( NetconfDocumentedException ex )
+ public static RpcError toRpcError( final NetconfDocumentedException ex )
{
StringBuilder infoBuilder = new StringBuilder();
Map<String, String> errorInfo = ex.getErrorInfo();
{
for( Entry<String,String> e: errorInfo.entrySet() ) {
infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() )
- .append( "</" ).append( e.getKey() ).append( '>' );
+ .append( "</" ).append( e.getKey() ).append( '>' );
}
}
return RpcErrors.getRpcError( null, ex.getErrorTag().getTagValue(), infoBuilder.toString(),
- toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(),
- toRpcErrorType( ex.getErrorType() ), ex.getCause() );
+ toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(),
+ toRpcErrorType( ex.getErrorType() ), ex.getCause() );
}
- private static ErrorSeverity toRpcErrorSeverity( NetconfDocumentedException.ErrorSeverity severity ) {
+ private static ErrorSeverity toRpcErrorSeverity( final NetconfDocumentedException.ErrorSeverity severity ) {
switch( severity ) {
- case warning:
- return RpcError.ErrorSeverity.WARNING;
- default:
- return RpcError.ErrorSeverity.ERROR;
+ case warning:
+ return RpcError.ErrorSeverity.WARNING;
+ default:
+ return RpcError.ErrorSeverity.ERROR;
}
}
- private static RpcError.ErrorType toRpcErrorType( NetconfDocumentedException.ErrorType type )
+ private static RpcError.ErrorType toRpcErrorType( final NetconfDocumentedException.ErrorType type )
{
switch( type ) {
- case protocol:
- return RpcError.ErrorType.PROTOCOL;
- case rpc:
- return RpcError.ErrorType.RPC;
- case transport:
- return RpcError.ErrorType.TRANSPORT;
- default:
- return RpcError.ErrorType.APPLICATION;
+ case protocol:
+ return RpcError.ErrorType.PROTOCOL;
+ case rpc:
+ return RpcError.ErrorType.RPC;
+ case transport:
+ return RpcError.ErrorType.TRANSPORT;
+ default:
+ return RpcError.ErrorType.APPLICATION;
}
}
public static CompositeNode flattenInput(final CompositeNode node) {
final QName inputQName = QName.create(node.getNodeType(), "input");
final CompositeNode input = node.getFirstCompositeByName(inputQName);
- if (input == null)
+ if (input == null) {
return node;
+ }
if (input instanceof CompositeNode) {
final List<Node<?>> nodes = ImmutableList.<Node<?>> builder() //
import opendaylight-md-sal-dom {prefix dom;}
import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
+ import ietf-inet-types {prefix inet; revision-date "2010-09-24";}
description
"Service definition for Binding Aware MD-SAL.";
when "/config:modules/config:module/config:type = 'sal-netconf-connector'";
leaf address {
- type string;
+ type inet:host;
}
leaf port {
- type uint32;
+ type inet:port-number;
}
leaf tcp-only {
container binding-registry {
uses config:service-ref {
refine type {
- // FIXME BUG-944 make mandatory (remove backwards compatibility)
- mandatory false;
+ mandatory true;
config:required-identity md-sal-binding:binding-broker-osgi-registry;
}
}
}
- // FIXME BUG-944 remove backwards compatibility
- // Deprecated, replaced by client dispatcher.
- // This dependency will be removed in near future and all configurations of netconf-connector need to be changed to use dispatcher dependency.
- container boss-thread-group {
- uses config:service-ref {
- refine type {
- mandatory false;
- config:required-identity netty:netty-threadgroup;
- }
- }
- }
-
- // FIXME BUG-944 remove backwards compatibility
- // Deprecated, replaced by client dispatcher.
- // This dependency will be removed in near future and all configurations of netconf-connector need to be changed to use dispatcher dependency.
- container worker-thread-group {
- uses config:service-ref {
- refine type {
- mandatory false;
- config:required-identity netty:netty-threadgroup;
- }
- }
- }
-
container event-executor {
uses config:service-ref {
refine type {
+ mandatory true;
config:required-identity netty:netty-event-executor;
}
}
container processing-executor {
uses config:service-ref {
refine type {
- // FIXME BUG-944 make mandatory (remove backwards compatibility)
- mandatory false;
+ mandatory true;
config:required-identity th:threadpool;
}
}
description "Makes up for flaws in netty threading design";
}
- // Replaces thread group dependencies
container client-dispatcher {
uses config:service-ref {
refine type {
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-parent</artifactId>
- <version>1.1-SNAPSHOT</version>
- <relativePath>../..</relativePath>
- </parent>
-
- <artifactId>sal-remoterpc-connector</artifactId>
- <packaging>bundle</packaging>
-
- <properties>
- <stax.version>1.0.1</stax.version>
- <zeromq.version>0.3.1</zeromq.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>sal-common-util</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>sal-connector-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <!-- MD Sal interdependencies -->
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>sal-core-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
-
- <!-- Tests -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <!-- AD Sal -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
-
- <!-- Yang tools -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-impl</artifactId>
- </dependency>
-
- <!-- Third Party -->
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.zeromq</groupId>
- <artifactId>jeromq</artifactId>
- <version>${zeromq.version}</version>
- </dependency>
-
- <dependency>
- <groupId>stax</groupId>
- <artifactId>stax-api</artifactId>
- <version>${stax.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Import-Package>*,
- !org.codehaus.enunciate.jaxrs</Import-Package>
- <Export-Package>org.opendaylight.controller.config.yang.md.sal.remote.rpc,
- org.opendaylight.controller.sal.connector.remoterpc.util,
- org.opendaylight.controller.sal.connector.remoterpc.dto,
- org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcClient,
- org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcServer,
- org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcProvider</Export-Package>
- <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
- </instructions>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-maven-plugin</artifactId>
- <executions>
- <execution>
- <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>
- <generator>
- <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
- <outputBaseDir>${salGeneratorPath}</outputBaseDir>
- </generator>
- <generator>
- <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
- <outputBaseDir>target/site/models</outputBaseDir>
- </generator>
-
- <generator>
- <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
- <outputBaseDir>target/site/models</outputBaseDir>
- </generator>
- </codeGenerators>
- <inspectDependencies>true</inspectDependencies>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
+++ /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.config.yang.md.sal.remote.rpc;
-
-import org.opendaylight.controller.sal.connector.remoterpc.ClientImpl;
-import org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcProvider;
-import org.opendaylight.controller.sal.connector.remoterpc.RoutingTableProvider;
-import org.opendaylight.controller.sal.connector.remoterpc.ServerImpl;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.osgi.framework.BundleContext;
-
-/**
- *
- */
-public final class ZeroMQServerModule
-extends org.opendaylight.controller.config.yang.md.sal.remote.rpc.AbstractZeroMQServerModule {
-
- private static final Integer ZEROMQ_ROUTER_PORT = 5554;
- private BundleContext bundleContext;
-
- public ZeroMQServerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
- final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- public ZeroMQServerModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
- final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
- final ZeroMQServerModule oldModule, final 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() {
-
- Broker broker = getDomBrokerDependency();
-
- final int port = getPort() != null ? getPort() : ZEROMQ_ROUTER_PORT;
-
- ServerImpl serverImpl = new ServerImpl(port);
-
- ClientImpl clientImpl = new ClientImpl();
-
- RoutingTableProvider provider = new RoutingTableProvider(bundleContext);//,serverImpl);
-
- RemoteRpcProvider facade = new RemoteRpcProvider(serverImpl, clientImpl);
- facade.setRoutingTableProvider(provider);
- facade.setContext(bundleContext);
- facade.setRpcProvisionRegistry((RpcProvisionRegistry) broker);
-
- broker.registerProvider(facade, bundleContext);
- return facade;
- }
-
- public void setBundleContext(final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-}
+++ /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.config.yang.md.sal.remote.rpc;
-
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
-import org.opendaylight.controller.config.spi.Module;
-import org.osgi.framework.BundleContext;
-
-/**
-*
-*/
-public class ZeroMQServerModuleFactory extends org.opendaylight.controller.config.yang.md.sal.remote.rpc.AbstractZeroMQServerModuleFactory
-{
-
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) {
- ZeroMQServerModule module = (ZeroMQServerModule) super.createModule(instanceName, dependencyResolver, bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-
- @Override
- public Module createModule(String instanceName, DependencyResolver dependencyResolver,
- DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception {
- ZeroMQServerModule module = (ZeroMQServerModule) super.createModule(instanceName, dependencyResolver, old,bundleContext);
- module.setBundleContext(bundleContext);
- return module;
- }
-}
+++ /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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-public class CapturedMessageHandler implements Runnable {
-
- private Logger _logger = LoggerFactory.getLogger(CapturedMessageHandler.class);
-
- private ZMQ.Socket socket;
-
- public CapturedMessageHandler(ZMQ.Socket socket){
- this.socket = socket;
- }
-
- @Override
- public void run(){
-
- try {
- while (!Thread.currentThread().isInterrupted()){
- String message = socket.recvStr();
- _logger.debug("Captured [{}]", message);
- }
- } catch (Exception e) {
- _logger.error("Exception raised [{}]", e.getMessage());
- }
- }
-}
+++ /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 static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-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.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-/**
- * An implementation of {@link org.opendaylight.controller.sal.core.api.RpcImplementation} that makes
- * remote RPC calls
- */
-public class ClientImpl implements RemoteRpcClient {
-
- private final Logger _logger = LoggerFactory.getLogger(ClientImpl.class);
-
- private final ZMQ.Context context = ZMQ.context(1);
- private final ClientRequestHandler handler;
- private RoutingTableProvider routingTableProvider;
-
- public ClientImpl(){
- handler = new ClientRequestHandler(context);
- start();
- }
-
- public ClientImpl(ClientRequestHandler handler){
- this.handler = handler;
- start();
- }
-
- public RoutingTableProvider getRoutingTableProvider() {
- return routingTableProvider;
- }
-
- @Override
- public void setRoutingTableProvider(RoutingTableProvider routingTableProvider) {
- this.routingTableProvider = routingTableProvider;
- }
-
- @Override
- public void start() {/*NOOPS*/}
-
- @Override
- public void stop() {
- closeZmqContext();
- handler.close();
- _logger.info("Stopped");
- }
-
- @Override
- public void close(){
- stop();
- }
-
- /**
- * Finds remote server that can execute this 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 remote service to be executed
- * @param input payload for the remote service
- * @return
- */
- public ListenableFuture<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 ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
-
- RouteIdentifierImpl routeId = new RouteIdentifierImpl();
- routeId.setType(rpc);
- routeId.setRoute(identifier);
-
- String address = lookupRemoteAddressForRpc(routeId);
-
- return sendMessage(input, routeId, address);
- }
-
- private ListenableFuture<RpcResult<CompositeNode>> sendMessage(CompositeNode input, RouteIdentifierImpl routeId, String address) {
- Message request = new Message.MessageBuilder()
- .type(Message.MessageType.REQUEST)
- .sender(Context.getInstance().getLocalUri())
- .recipient(address)
- .route(routeId)
- .payload(XmlUtils.compositeNodeToXml(input))
- .build();
-
- List<RpcError> errors = new ArrayList<RpcError>();
-
- try{
- Message response = handler.handle(request);
- CompositeNode payload = null;
-
- if ( response != null ) {
-
- _logger.info("Received response [{}]", response);
-
- Object rawPayload = response.getPayload();
- switch (response.getType()) {
- case ERROR:
- if ( rawPayload instanceof List )
- errors = (List) rawPayload;
- break;
-
- case RESPONSE:
- payload = XmlUtils.xmlToCompositeNode((String) rawPayload);
- break;
-
- default:
- errors.add(
- RpcErrors.getRpcError(null, null,null,null,"Unable to get response from remote controller", null, null)
- );
- break;
-
- }
- }
- return Futures.immediateFuture(Rpcs.getRpcResult(true, payload, errors));
-
- } catch (Exception e){
- collectErrors(e, errors);
- return Futures.immediateFuture(Rpcs.<CompositeNode>getRpcResult(false, null, errors));
- }
- }
-
- /**
- * Find address for the given route identifier in routing table
- * @param routeId route identifier
- * @return remote network address
- */
- private String lookupRemoteAddressForGlobalRpc(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 = 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 = routingTable.get().getLastAddedRoute(routeId);
- checkState(address != null, "Address not found for route [%s]", routeId);
-
- return address;
- }
-
- private void collectErrors(Exception e, List<RpcError> errors){
- if (e == null) return;
- if (errors == null) errors = new ArrayList<RpcError>();
-
- errors.add(RpcErrors.getRpcError(null, null, null, null, e.getMessage(), null, e.getCause()));
- for (Throwable t : e.getSuppressed()) {
- errors.add(RpcErrors.getRpcError(null, null, null, null, t.getMessage(), null, t));
- }
- }
-
- /**
- * Closes ZMQ Context. It tries to gracefully terminate the context. If
- * termination takes more than a second, its forcefully shutdown.
- */
- private void closeZmqContext() {
- ExecutorService exec = Executors.newSingleThreadExecutor();
- FutureTask<?> zmqTermination = new FutureTask<Void>(new Runnable() {
-
- @Override
- public void run() {
- try {
- if (context != null)
- context.term();
- _logger.debug("ZMQ Context terminated");
- } catch (Exception e) {
- _logger.debug("ZMQ Context termination threw exception [{}]. Continuing shutdown...", e);
- }
- }
- }, null);
-
- exec.execute(zmqTermination);
-
- try {
- zmqTermination.get(1L, TimeUnit.SECONDS);
- } catch (Exception e) {/*ignore and continue with shutdown*/}
-
- exec.shutdownNow();
- }
-}
+++ /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 com.google.common.base.Preconditions;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- *
- */
-class ClientRequestHandler implements AutoCloseable{
-
- private Logger _logger = LoggerFactory.getLogger(ClientRequestHandler.class);
- private final String DEFAULT_NAME = "remoterpc-client-worker";
- private final String INPROC_PROTOCOL_PREFIX = "inproc://";
- private final String TCP_PROTOCOL_PREFIX = "tcp://";
-
- private ZMQ.Context context;
-
- /*
- * Worker thread pool. Each thread runs a ROUTER-DEALER pair
- */
- private ExecutorService workerPool;
-
- /*
- * Set of remote servers this client is currently connected to
- */
- private Map<String, String> connectedServers;
-
- protected ClientRequestHandler(ZMQ.Context context) {
- this.context = context;
- connectedServers = new ConcurrentHashMap<String, String>();
- start();
- }
-
- /**
- * Starts a pool of worker as needed. A worker thread that has not been used for 5 min
- * is terminated and removed from the pool. If thread dies due to an exception, its
- * restarted.
- */
- private void start(){
-
- workerPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
- 5L, TimeUnit.MINUTES,
- new SynchronousQueue<Runnable>()){
-
- @Override
- protected void afterExecute(Runnable r, Throwable t) {
- if (isTerminating() || isTerminated() || isShutdown())
- return;
-
- Worker worker = (Worker) r;
- Preconditions.checkState( worker != null );
- String remoteServerAddress = worker.getRemoteServerAddress();
- connectedServers.remove(remoteServerAddress);
-
- if ( t != null ){
- _logger.debug("Exception caught while terminating worker [{},{}]. " +
- "Restarting worker...", t.getClass(), t.getMessage());
-
- connectedServers.put(remoteServerAddress, remoteServerAddress);
- this.execute(r);
- }
- super.afterExecute(r, null);
- }
- };
- }
-
- public Message handle(Message request) throws IOException, ClassNotFoundException, InterruptedException {
-
- String remoteServerAddress = request.getRecipient();
- //if we already have router-dealer bridge setup for this address the send request
- //otherwise first create the bridge and then send request
- if ( connectedServers.containsKey(remoteServerAddress) )
- return sendMessage(request, remoteServerAddress);
-
- else{
- workerPool.execute(new Worker(remoteServerAddress));
- connectedServers.put(remoteServerAddress, remoteServerAddress);
- //give little time for sockets to get initialized
- //TODO: Add socket ping-pong message to ensure socket init rather than thread.sleep.
- Thread.sleep(1000);
- return sendMessage(request, remoteServerAddress);
- }
- }
-
- private Message sendMessage(Message request, String address) throws IOException, ClassNotFoundException {
- Message response = null;
- ZMQ.Socket socket = context.socket(ZMQ.REQ);
-
- try {
- String inProcessSocketAddress = INPROC_PROTOCOL_PREFIX + address;
- socket.connect( inProcessSocketAddress );
- _logger.debug("Sending request [{}]", request);
- socket.send(Message.serialize(request));
- _logger.info("Request sent. Waiting for reply...");
- byte[] reply = socket.recv(0);
- _logger.info("Response received");
- response = (Message) Message.deserialize(reply);
- _logger.debug("Response [{}]", response);
- } finally {
- socket.close();
- }
- return response;
- }
-
- /**
- * This gets called automatically if used with try-with-resources
- */
- @Override
- public void close(){
- workerPool.shutdown();
- _logger.info("Request Handler closed");
- }
-
- /**
- * Total number of workers in the pool. Number of workers represent
- * number of remote servers {@link org.opendaylight.controller.sal.connector.remoterpc.ClientImpl} is connected to.
- *
- * @return worker count
- */
- public int getWorkerCount(){
-
- if (workerPool == null) return 0;
-
- return ((ThreadPoolExecutor)workerPool).getActiveCount();
- }
- /**
- * Handles RPC request
- */
- private class Worker implements Runnable {
- private String name;
- private String remoteServer; //<serverip:rpc-port>
-
- public Worker(String address){
- this.name = DEFAULT_NAME + "[" + address + "]";
- this.remoteServer = address;
- }
-
- public String getRemoteServerAddress(){
- return this.remoteServer;
- }
-
- @Override
- public void run() {
- Thread.currentThread().setName(name);
- _logger.debug("Starting ... ");
-
- ZMQ.Socket router = context.socket(ZMQ.ROUTER);
- ZMQ.Socket dealer = context.socket(ZMQ.DEALER);
-
- try {
- int success = router.bind(INPROC_PROTOCOL_PREFIX + remoteServer);
- Preconditions.checkState(-1 != success, "Could not bind to " + remoteServer);
-
- dealer.connect(TCP_PROTOCOL_PREFIX + remoteServer);
-
- _logger.info("Worker started for [{}]", remoteServer);
-
- //TODO: Add capture handler
- //This code will block until the zmq context is terminated.
- ZMQ.proxy(router, dealer, null);
-
- } catch (Exception e) {
- _logger.debug("Ignoring exception [{}, {}]", e.getClass(), e.getMessage());
- } finally {
- try {
- router.close();
- dealer.close();
- } catch (Exception x) {
- _logger.debug("Exception while closing socket [{}]", x);
- }
- _logger.debug("Closing...");
- }
- }
- }
-}
+++ /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.Inet4Address;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.Enumeration;
-
-import org.zeromq.ZMQ;
-
-/**
- * Provides a ZeroMQ Context object
- */
-public class Context {
- private final ZMQ.Context zmqContext = ZMQ.context(1);
- private String uri;
- private final String DEFAULT_RPC_PORT = "5554";
-
- private static Context _instance = new Context();
-
- private Context() {}
-
- public static Context getInstance(){
- return _instance;
- }
-
- public ZMQ.Context getZmqContext(){
- return this.zmqContext;
- }
-
- public String getLocalUri(){
- uri = (uri != null) ? uri
- : new StringBuilder().append(getIpAddress()).append(":")
- .append(getRpcPort()).toString();
-
- return uri;
- }
-
- public String getRpcPort(){
- String rpcPort = (System.getProperty("rpc.port") != null)
- ? System.getProperty("rpc.port")
- : DEFAULT_RPC_PORT;
-
- return rpcPort;
- }
-
- private String getIpAddress(){
- String ipAddress = (System.getProperty("local.ip") != null)
- ? System.getProperty("local.ip")
- : findIpAddress();
-
- return ipAddress;
- }
-
- /**
- * Finds IPv4 address of the local VM
- * TODO: This method is non-deterministic. There may be more than one IPv4 address. Cant say which
- * address will be returned. Read IP from a property file or enhance the code to make it deterministic.
- * Should we use IP or hostname?
- *
- * @return
- */
- private String findIpAddress() {
- String hostAddress = null;
- Enumeration<?> e = null;
- try {
- e = NetworkInterface.getNetworkInterfaces();
- } catch (SocketException e1) {
- e1.printStackTrace();
- }
- while (e.hasMoreElements()) {
-
- NetworkInterface n = (NetworkInterface) e.nextElement();
-
- Enumeration<?> ee = n.getInetAddresses();
- while (ee.hasMoreElements()) {
- InetAddress i = (InetAddress) ee.nextElement();
- if ((i instanceof Inet4Address) && (i.isSiteLocalAddress()))
- hostAddress = i.getHostAddress();
- }
- }
- return hostAddress;
-
- }
-}
+++ /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;
-
-public interface RemoteRpcClient extends AutoCloseable{
-
- void setRoutingTableProvider(RoutingTableProvider provider);
-
- void stop();
-
- void start();
-}
+++ /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 static com.google.common.base.Preconditions.checkState;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-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 com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
-
-public class RemoteRpcProvider implements
- RpcImplementation,
- RoutedRpcDefaultImplementation,
- AutoCloseable,
- Provider {
-
- private final Logger _logger = LoggerFactory.getLogger(RemoteRpcProvider.class);
-
- 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 ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
- return client.invokeRpc(rpc, input);
- }
-
- @Override
- public ListenableFuture<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);
- }
- }
-
- /**
- * 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();
-
- checkState(routingTable.isPresent(), "Routing table is null");
-
- return routingTable.get();
- }
-
- /**
- * Listener for rpc registrations in broker
- */
- private class RpcListener implements RpcRegistrationListener {
-
- @Override
- 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 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 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);
- }
- }
-
- /**
- *
- * @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);
- }
- }
-
- /**
- *
- * @param changes
- * @return
- */
- private Set<RpcRouter.RouteIdentifier<?, ?, ?>> getRouteIdentifiers(Map<RpcRoutingContext, Set<InstanceIdentifier>> changes) {
- RouteIdentifierImpl routeId = null;
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIdSet = new HashSet<>();
-
- 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;
- }
-
- }
-}
+++ /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 org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
-import org.opendaylight.controller.sal.connector.remoterpc.impl.RoutingTableImpl;
-import org.osgi.framework.BundleContext;
-import org.osgi.util.tracker.ServiceTracker;
-
-import com.google.common.base.Optional;
-
-public class RoutingTableProvider implements AutoCloseable {
-
- @SuppressWarnings("rawtypes")
- final ServiceTracker<RoutingTable,RoutingTable> tracker;
-
- private RoutingTableImpl<?, ?> routingTableImpl = null;
-
- //final private RouteChangeListener routeChangeListener;
-
-
- public RoutingTableProvider(BundleContext ctx){//,RouteChangeListener rcl) {
- @SuppressWarnings("rawtypes")
- ServiceTracker<RoutingTable, RoutingTable> rawTracker = new ServiceTracker<>(ctx, RoutingTable.class, null);
- tracker = rawTracker;
- tracker.open();
-
- //routeChangeListener = rcl;
- }
-
- public Optional<RoutingTable<RpcRouter.RouteIdentifier<?, ?, ?>, String>> getRoutingTable() {
- @SuppressWarnings("unchecked")
- RoutingTable<RpcRouter.RouteIdentifier<?, ?, ?>, String> tracked = tracker.getService();
-
- if(tracked instanceof RoutingTableImpl){
- if(routingTableImpl != tracked){
- routingTableImpl= (RoutingTableImpl<?, ?>)tracked;
- //routingTableImpl.setRouteChangeListener(routeChangeListener);
- }
- }
-
- return Optional.fromNullable(tracked);
- }
-
- @Override
- public void close() throws Exception {
- tracker.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.sal.connector.remoterpc;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-
-/**
- * ZeroMq based implementation of RpcRouter.
- */
-public class ServerImpl implements RemoteRpcServer {
-
- private final Logger _logger = LoggerFactory.getLogger(ServerImpl.class);
-
- private ExecutorService serverPool;
- protected ServerRequestHandler handler;
-
- private Set<QName> remoteServices;
- private ProviderSession brokerSession;
- private ZMQ.Context context;
-
- 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 volatile State status = State.STOPPED;
-
- private String serverAddress;
- private final int port;
-
- public static enum State {
- STARTING, STARTED, STOPPED;
- }
-
- public ServerImpl(int port) {
- this.port = port;
- }
-
- public State getStatus() {
- return this.status;
- }
-
- public Optional<ServerRequestHandler> getHandler() {
- return Optional.fromNullable(this.handler);
- }
-
- public void setBrokerSession(ProviderSession session) {
- this.brokerSession = session;
- }
-
- public Optional<ProviderSession> getBrokerSession() {
- return Optional.fromNullable(this.brokerSession);
- }
-
- public Optional<ZMQ.Context> getZmqContext() {
- return Optional.fromNullable(this.context);
- }
-
- public String getServerAddress() {
- return serverAddress;
- }
-
- public String getHandlerAddress() {
- return HANDLER_INPROC_ADDRESS;
- }
-
- /**
- *
- */
- public void start() {
- Preconditions.checkState(State.STOPPED == this.getStatus(),
- "Remote RPC Server is already running");
-
- status = State.STARTING;
- _logger.debug("Remote RPC Server is starting...");
-
- String hostIpAddress = findIpAddress();
-
- //Log and silently die as per discussion in the bug (bug-362)
- //https://bugs.opendaylight.org/show_bug.cgi?id=362
- //
- // A tracking enhancement defect (bug-366) is created to properly fix this issue
- //https://bugs.opendaylight.org/show_bug.cgi?id=366
- //checkState(hostIpAddress != null, "Remote RPC Server could not acquire host ip address");
-
- if (hostIpAddress == null) {
- _logger.error("Remote RPC Server could not acquire host ip address. Stopping...");
- stop();
- return;
- }
-
- this.serverAddress = new StringBuilder(hostIpAddress).
- append(":").
- append(port).
- toString();
-
- context = ZMQ.context(1);
- remoteServices = new HashSet<QName>();//
- serverPool = Executors.newSingleThreadExecutor();//main server thread
- serverPool.execute(receive()); // Start listening rpc requests
-
- status = State.STARTED;
- _logger.info("Remote RPC Server started [{}]", getServerAddress());
- }
-
- public void stop(){
- close();
- }
-
- /**
- *
- */
- @Override
- public void close() {
-
- if (State.STOPPED == this.getStatus()) return; //do nothing
-
- if (serverPool != null)
- serverPool.shutdown();
-
- closeZmqContext();
-
- status = State.STOPPED;
- _logger.info("Remote RPC Server stopped");
- }
-
- /**
- * Closes ZMQ Context. It tries to gracefully terminate the context. If
- * termination takes more than 5 seconds, its forcefully shutdown.
- */
- private void closeZmqContext() {
- ExecutorService exec = Executors.newSingleThreadExecutor();
- FutureTask<?> zmqTermination = new FutureTask<Void>(new Runnable() {
-
- @Override
- public void run() {
- try {
- if (context != null)
- context.term();
- _logger.debug("ZMQ Context terminated gracefully!");
- } catch (Exception e) {
- _logger.debug("ZMQ Context termination threw exception [{}]. Continuing shutdown...", e);
- }
- }
- }, null);
-
- exec.execute(zmqTermination);
-
- try {
- zmqTermination.get(5L, TimeUnit.SECONDS);
- } catch (Exception e) {/*ignore and continue with shutdown*/}
-
- exec.shutdownNow();
- }
-
- /**
- * Main listener thread that spawns {@link ServerRequestHandler} as workers.
- *
- * @return
- */
- private Runnable receive() {
- return new Runnable() {
-
- @Override
- public void run() {
- Thread.currentThread().setName("remote-rpc-server");
- _logger.debug("Remote RPC Server main thread starting...");
-
- //socket clients connect to (frontend)
- ZMQ.Socket clients = context.socket(ZMQ.ROUTER);
-
- //socket RequestHandlers connect to (backend)
- ZMQ.Socket workers = context.socket(ZMQ.DEALER);
-
- try (SocketPair capturePair = new SocketPair();
- ServerRequestHandler requestHandler = new ServerRequestHandler(context,
- brokerSession,
- HANDLER_WORKER_COUNT,
- HANDLER_INPROC_ADDRESS,
- getServerAddress());) {
-
- handler = requestHandler;
- clients.setHWM(HWM);
- clients.bind("tcp://*:" + port);
- workers.setHWM(HWM);
- workers.bind(HANDLER_INPROC_ADDRESS);
- //start worker threads
- _logger.debug("Remote RPC Server worker threads starting...");
- requestHandler.start();
- //start capture thread
- // handlerPool.execute(new CaptureHandler(capturePair.getReceiver()));
- // Connect work threads to client threads via a queue
- ZMQ.proxy(clients, workers, null);//capturePair.getSender());
-
- } catch (Exception e) {
- _logger.debug("Unhandled exception [{}, {}]", e.getClass(), e.getMessage());
- } finally {
- if (clients != null) clients.close();
- if (workers != null) workers.close();
- _logger.info("Remote RPC Server stopped");
- }
- }
- };
- }
-
- /**
- * Finds IPv4 address of the local VM
- * TODO: This method is non-deterministic. There may be more than one IPv4 address. Cant say which
- * address will be returned. Read IP from a property file or enhance the code to make it deterministic.
- * Should we use IP or hostname?
- *
- * @return
- */
- private String findIpAddress() {
- Enumeration<?> e = null;
- try {
- e = NetworkInterface.getNetworkInterfaces();
- } catch (SocketException e1) {
- _logger.error("Failed to get list of interfaces", e1);
- return null;
- }
- while (e.hasMoreElements()) {
-
- NetworkInterface n = (NetworkInterface) e.nextElement();
-
- Enumeration<?> ee = n.getInetAddresses();
- while (ee.hasMoreElements()) {
- InetAddress i = (InetAddress) ee.nextElement();
- _logger.debug("Trying address {}", i);
- if ((i instanceof Inet4Address) && (!i.isLoopbackAddress())) {
- String hostAddress = i.getHostAddress();
- _logger.debug("Settled on host address {}", hostAddress);
- return hostAddress;
- }
- }
- }
-
- _logger.error("Failed to find a suitable host address");
- return 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.sal.connector.remoterpc;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-/**
- *
- */
-public class ServerRequestHandler implements AutoCloseable{
-
- private final Logger _logger = LoggerFactory.getLogger(ServerRequestHandler.class);
- private final String DEFAULT_NAME = "remote-rpc-worker";
- private final String dealerAddress;
- private final String serverAddress;
- private final int workerCount;
- private final ZMQ.Context context;
- private final Broker.ProviderSession broker;
-
- private RequestHandlerThreadPool workerPool;
- private final AtomicInteger threadId = new AtomicInteger();
-
- public ServerRequestHandler(ZMQ.Context context,
- Broker.ProviderSession session,
- int workerCount,
- String dealerAddress,
- String serverAddress) {
- this.context = context;
- this.dealerAddress = dealerAddress;
- this.serverAddress = serverAddress;
- this.broker = session;
- this.workerCount = workerCount;
- }
-
- public ThreadPoolExecutor getWorkerPool(){
- return workerPool;
- }
-
- public void start(){
- workerPool = new RequestHandlerThreadPool(
- workerCount, workerCount,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- //unbound is ok. Task will never be submitted
-
- for (int i=0;i<workerCount;i++){
- workerPool.execute(new Worker(threadId.incrementAndGet()));
- }
- }
-
- /**
- * This gets called automatically if used with try-with-resources
- * @throws Exception
- */
- @Override
- public void close() throws Exception {
- if (workerPool != null)
- workerPool.shutdown();
- _logger.info("Request Handler closed");
- }
-
- /**
- * Worker to handles RPC request
- */
- private class Worker implements Runnable {
- private final String name;
-
- public Worker(int id){
- this.name = DEFAULT_NAME + "-" + id;
- }
-
- @Override
- public void run() {
- Thread.currentThread().setName(name);
- _logger.debug("Starting... ");
- ZMQ.Socket socket = null;
-
- try {
- socket = context.socket(ZMQ.REP);
- socket.connect(dealerAddress);
-
- while (!Thread.currentThread().isInterrupted()) {
-
- MessageHandler handler = new MessageHandler(socket);
- handler.receiveMessage();
-
- if (handler.hasMessageForBroker()) {
-
- Message request = handler.getMessage();
- Future<RpcResult<CompositeNode>> rpc = null;
- RpcResult<CompositeNode> result = null;
-
- //TODO Call this in a new thread with timeout
- try {
- rpc = broker.rpc(
- (QName) request.getRoute().getType(),
- XmlUtils.xmlToCompositeNode((String) request.getPayload()));
-
- result = (rpc != null) ? rpc.get() : null;
-
- handler.sendResponse(result);
-
- } catch (Exception e) {
- _logger.debug("Broker threw [{}]", e);
- handler.sendError(e.getMessage());
- }
- }
-
- }
- } catch (Exception e) {
- printException(e);
- } finally {
- closeSocket(socket);
- }
- }
-
- private void printException(Exception e) {
- try (StringWriter s = new StringWriter();
- PrintWriter p = new PrintWriter(s)) {
- e.printStackTrace(p);
- _logger.debug(s.toString());
- } catch (IOException e1) {/*Ignore and continue*/ }
- }
-
- private void closeSocket(ZMQ.Socket socket) {
- try {
- if (socket != null) socket.close();
- } catch (Exception x) {
- _logger.debug("Exception while closing socket [{}]", x);
- } finally {
- if (socket != null) socket.close();
- }
- _logger.debug("Closing...");
- }
- }
-
-
- /**
- *
- */
- public class RequestHandlerThreadPool extends ThreadPoolExecutor{
-
- public RequestHandlerThreadPool(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- }
-
- @Override
- protected void afterExecute(Runnable r, Throwable t) {
- if (isTerminating() || isTerminated() || isShutdown())
- return;
-
- if ( t != null ){
- _logger.debug("Exception caught while terminating worker [{},{}]", t.getClass(), t.getMessage());
- }
-
- this.execute(new Worker(threadId.incrementAndGet()));
- super.afterExecute(r, null);
- }
- }
-
- class MessageHandler{
- private final ZMQ.Socket socket;
- private Message message; //parsed message received on zmq server port
- private boolean messageForBroker = false; //if the message is valid and not a "ping" message
-
- public MessageHandler(ZMQ.Socket socket){
- this.socket = socket;
- }
-
- void receiveMessage(){
- byte[] bytes = socket.recv(); //this blocks
- _logger.debug("Received bytes:[{}]", bytes.length);
-
- Object objectRecvd = null;
- try{
- objectRecvd = Message.deserialize(bytes);
- }catch (Exception e){
- sendError(e.getMessage());
- return;
- }
-
- if (!(objectRecvd instanceof Message)) {
- sendError("Invalid message received");
- return;
- }
-
- message = (Message) objectRecvd;
-
- _logger.info("Received request [{}]", message);
-
- if (Message.MessageType.PING == message.getType()){
- sendPong();
- return;
- }
-
- messageForBroker = true;
- }
-
- boolean hasMessageForBroker(){
- return messageForBroker;
- }
-
- Message getMessage(){
- return message;
- }
-
- void sendResponse(RpcResult<CompositeNode> result){
- CompositeNode payload = (result != null) ? result.getResult() : null;
-
- String recipient = null;
- RpcRouter.RouteIdentifier<?, ?, ?> routeId = null;
-
- if (message != null) {
- recipient = message.getSender();
- routeId = message.getRoute();
- }
-
- Message response = new Message.MessageBuilder()
- .type(Message.MessageType.RESPONSE)
- .sender(serverAddress)
- .recipient(recipient)
- .route(routeId)
- .payload(XmlUtils.compositeNodeToXml(payload))
- .build();
-
- send(response);
- }
-
- private void sendError(String msg){
- Message errorResponse = new Message.MessageBuilder()
- .type(Message.MessageType.ERROR)
- .sender(serverAddress)
- .payload(msg)
- .build();
-
- send(errorResponse);
- }
-
- private void sendPong(){
- Message pong = new Message.MessageBuilder()
- .type(Message.MessageType.PONG)
- .sender(serverAddress)
- .build();
-
- send(pong);
- }
-
- private void send(Message msg){
- byte[] serializedMessage = null;
- try {
- serializedMessage = Message.serialize(msg);
- } catch (Exception e) {
- _logger.debug("Unexpected error during serialization of response [{}]", msg);
- return;
- }
-
- if (serializedMessage != null)
- if (socket.send(serializedMessage))
- _logger.info("Response sent [{}]", msg);
- else _logger.debug("Failed to send serialized message");
- }
- }
-}
+++ /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.connector.remoterpc;
-
-import org.zeromq.ZMQ;
-
-import java.util.UUID;
-
-/**
- *
- */
-public class SocketPair implements AutoCloseable{
- private ZMQ.Socket sender;
- private ZMQ.Socket receiver;
-
- private static final String INPROC_PREFIX = "inproc://";
-
- public SocketPair(){
- String address = new StringBuilder(INPROC_PREFIX)
- .append(UUID.randomUUID())
- .toString();
-
- receiver = Context.getInstance().getZmqContext().socket(ZMQ.PAIR);
- receiver.bind(address);
-
- sender = Context.getInstance().getZmqContext().socket(ZMQ.PAIR);
- sender.connect(address);
- }
-
- public ZMQ.Socket getSender(){
- return this.sender;
- }
-
- public ZMQ.Socket getReceiver(){
- return this.receiver;
- }
-
- @Override
- public void close() throws Exception {
- sender.close();
- receiver.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.sal.connector.remoterpc.dto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-
-public class Message implements Serializable {
- private static final long serialVersionUID = 1L;
-
- public static enum MessageType {
- PING((byte) 0),
- PONG((byte) 1),
- REQUEST((byte) 2),
- RESPONSE((byte) 3),
- ERROR((byte)4);
-
- private final byte type;
-
- MessageType(byte type) {
- this.type = type;
- }
-
- public byte getType(){
- return this.type;
- }
- }
-
- private MessageType type;
- private String sender;
- private String recipient;
- private RpcRouter.RouteIdentifier<?, ?, ?> route;
- private Object payload;
-
- public MessageType getType() {
- return type;
- }
-
- public void setType(MessageType type) {
- this.type = type;
- }
-
- public String getSender() {
- return sender;
- }
-
- public void setSender(String sender) {
- this.sender = sender;
- }
-
- public RpcRouter.RouteIdentifier<?, ?, ?> getRoute() {
- return route;
- }
-
- public void setRoute(RpcRouter.RouteIdentifier<?, ?, ?> route) {
- this.route = route;
- }
-
- public Object getPayload() {
- return payload;
- }
-
- public void setPayload(Object payload) {
- this.payload = payload;
- }
-
- public String getRecipient() {
- return recipient;
- }
-
- public void setRecipient(String recipient) {
- this.recipient = recipient;
- }
-
- @Override
- public String toString() {
- return "Message{" +
- "type=" + type +
- ", sender='" + sender + '\'' +
- ", recipient='" + recipient + '\'' +
- ", route=" + route +
- ", payload=" + payload +
- '}';
- }
-
- /**
- * Converts any {@link Serializable} object to byte[]
- *
- * @param obj
- * @return
- * @throws IOException
- */
- public static byte[] serialize(Object obj) throws IOException {
- ByteArrayOutputStream b = new ByteArrayOutputStream();
- ObjectOutputStream o = new ObjectOutputStream(b);
- o.writeObject(obj);
- return b.toByteArray();
- }
-
- /**
- * Converts byte[] to a java object
- *
- * @param bytes
- * @return
- * @throws IOException
- * @throws ClassNotFoundException
- */
- public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
- ByteArrayInputStream b = new ByteArrayInputStream(bytes);
- ObjectInputStream o = new ObjectInputStream(b);
- return o.readObject();
- }
-
- public static class Response extends Message implements RpcRouter.RpcReply<Object> {
- private static final long serialVersionUID = 1L;
- private ResponseCode code; // response code
-
- public static enum ResponseCode {
- SUCCESS(200), BADREQUEST(400), TIMEOUT(408), GONE(410), SERVERERROR(500), SERVICEUNAVAILABLE(503);
-
- private final int code;
-
- ResponseCode(int code) {
- this.code = code;
- }
- }
-
- public ResponseCode getCode() {
- return code;
- }
-
- public void setCode(ResponseCode code) {
- this.code = code;
- }
- }
-
- /**
- * Builds a {@link Message} object
- */
- public static class MessageBuilder{
-
- private final Message message;
-
- public MessageBuilder(){
- message = new Message();
- }
-
-
- public MessageBuilder type(MessageType type){
- message.setType(type);
- return this;
- }
-
- public MessageBuilder sender(String sender){
- message.setSender(sender);
- return this;
- }
-
- public MessageBuilder recipient(String recipient){
- message.setRecipient(recipient);
- return this;
- }
-
- public MessageBuilder route(RpcRouter.RouteIdentifier<?, ?, ?> route){
- message.setRoute(route);
- return this;
- }
-
- public MessageBuilder payload(Object obj){
- message.setPayload(obj);
- return this;
- }
-
- public Message build(){
- return message;
- }
- }
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.connector.remoterpc.dto;
-
-import org.zeromq.ZMQ;
-
-/**
- * A class encapsulating {@link Message} and the {@link ZMQ.Socket} over which it is transmitted
- */
-public class MessageWrapper {
-
- private Message _message;
- private ZMQ.Socket _receiveSocket;
-
- public MessageWrapper(Message message, ZMQ.Socket receiveSocket) {
- this._message = message;
- this._receiveSocket = receiveSocket;
- }
-
- public Message getMessage() {
- return _message;
- }
-
- public ZMQ.Socket getReceiveSocket() {
- return _receiveSocket;
- }
-}
+++ /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.dto;
-
-import java.io.Serializable;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
-public class RouteIdentifierImpl implements RpcRouter.RouteIdentifier<QName, QName, InstanceIdentifier>,Serializable {
- private static final long serialVersionUID = 1L;
-
- private QName context;
- private QName type;
- private InstanceIdentifier route;
-
- @Override
- public QName getContext() {
- return this.context;
- }
-
- @Override
- public QName getType() {
- return this.type;
- }
-
- @Override
- public InstanceIdentifier getRoute() {
- return this.route;
- }
-
- public void setContext(QName context) {
- this.context = context;
- }
-
- public void setType(QName type) {
- this.type = type;
- }
-
- public void setRoute(InstanceIdentifier route) {
- this.route = route;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- RouteIdentifierImpl that = (RouteIdentifierImpl) o;
-
- if (context == null){
- if (that.getContext() != null) return false;
- }else
- if (!context.equals(that.context)) return false;
-
- if (route == null){
- if (that.getRoute() != null) return false;
- }else
- if (!route.equals(that.route)) return false;
-
- if (type == null){
- if (that.getType() != null) return false;
- }else
- if (!type.equals(that.type)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int prime = 31;
- int result = 0;
- result = prime * result + (context == null ? 0:context.hashCode());
- result = prime * result + (type == null ? 0:type.hashCode());
- result = prime * result + (route == null ? 0:route.hashCode());
- return result;
- }
-
- @Override
- public String toString() {
- return "RouteIdentifierImpl{" +
- "context=" + context +
- ", type=" + type +
- ", route=" + route +
- '}';
- }
-}
+++ /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.util;
-
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeUtils;
-import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.StringWriter;
-
-public class XmlUtils {
-
- private static final Logger _logger = LoggerFactory.getLogger(XmlUtils.class);
-
- public static String compositeNodeToXml(CompositeNode cNode){
- if (cNode == null) return new String();
-
- Document domTree = NodeUtils.buildShadowDomTree(cNode);
- StringWriter writer = new StringWriter();
- try {
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- transformer.transform(new DOMSource(domTree), new StreamResult(writer));
- } catch (TransformerException e) {
- _logger.error("Error during translation of Document to OutputStream", e);
- }
-
- return writer.toString();
- }
-
- public static CompositeNode xmlToCompositeNode(String xml){
- if (xml==null || xml.length()==0) return null;
-
- Node<?> dataTree;
- try {
- dataTree = XmlTreeBuilder.buildDataTree(new ByteArrayInputStream(xml.getBytes()));
- } catch (XMLStreamException e) {
- _logger.error("Error during building data tree from XML", e);
- return null;
- }
- if (dataTree == null) {
- _logger.error("data tree is null");
- return null;
- }
- if (dataTree instanceof SimpleNode) {
- _logger.error("RPC XML was resolved as SimpleNode");
- return null;
- }
- return (CompositeNode) dataTree;
- }
-}
+++ /dev/null
-module odl-sal-dom-rpc-remote-cfg {
- yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc";
- prefix "rpc-cluster";
-
- import config { prefix config; revision-date 2013-04-05; }
- import opendaylight-md-sal-dom {prefix dom;}
-
- description
- "Service definition for Binding Aware MD-SAL.";
-
- revision "2013-10-28" {
- description
- "Initial revision";
- }
-
- identity remote-rpc-server {
- base config:service-type;
- config:java-class "org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcServer";
- }
-
- identity remote-rpc-client {
- base config:service-type;
- config:java-class "org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcClient";
- }
-
- identity remote-zeromq-rpc-server {
- base config:module-type;
- config:java-name-prefix ZeroMQServer;
- }
-
- augment "/config:modules/config:module/config:configuration" {
- case remote-zeromq-rpc-server {
- when "/config:modules/config:module/config:type = 'remote-zeromq-rpc-server'";
-
- container dom-broker {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity dom:dom-broker-osgi-registry;
- }
- }
- }
-
- leaf port {
- type uint16;
- }
- }
- }
-}
\ No newline at end of file
+++ /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.connector.remoterpc;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.io.IOException;
-
-import junit.framework.Assert;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.api.RoutingTable;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.opendaylight.controller.sal.connector.remoterpc.utils.MessagingUtil;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-
-import com.google.common.base.Optional;
-
-/**
- *
- */
-public class ClientImplTest {
- RoutingTableProvider routingTableProvider;
- ClientImpl client;
- ClientRequestHandler mockHandler;
-
- @Before
- public void setUp() throws Exception {
-
- //mock routing table
- routingTableProvider = mock(RoutingTableProvider.class);
- 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
- mockHandler = mock(ClientRequestHandler.class);
-
- client = new ClientImpl(mockHandler);
- client.setRoutingTableProvider(routingTableProvider);
-
- }
-
- @After
- public void tearDown() throws Exception {
-
- }
-
- @Test
- public void getRoutingTableProvider_Call_ShouldReturnMockProvider() throws Exception {
- Assert.assertEquals(routingTableProvider, client.getRoutingTableProvider());
-
- }
-
- @Test
- public void testStart() throws Exception {
-
- }
-
- @Test
- public void testStop() throws Exception {
-
- }
-
- @Test
- public void testClose() throws Exception {
-
- }
-
- //@Test
- public void invokeRpc_NormalCall_ShouldReturnSuccess() throws Exception {
-
- when(mockHandler.handle(any(Message.class))).
- thenReturn(MessagingUtil.createEmptyMessage());
-
- RpcResult<CompositeNode> result = client.invokeRpc(null, null).get();
-
- Assert.assertTrue(result.isSuccessful());
- Assert.assertTrue(result.getErrors().isEmpty());
- Assert.assertNull(result.getResult());
- }
-
- //@Test
- public void invokeRpc_HandlerThrowsException_ShouldReturnError() throws Exception {
-
- when(mockHandler.handle(any(Message.class))).
- thenThrow(new IOException());
-
- RpcResult<CompositeNode> result = client.invokeRpc(null, null).get();
-
- Assert.assertFalse(result.isSuccessful());
- Assert.assertFalse(result.getErrors().isEmpty());
- Assert.assertNull(result.getResult());
- }
-
-}
+++ /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.connector.remoterpc;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-import junit.framework.Assert;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.opendaylight.controller.sal.connector.remoterpc.utils.MessagingUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-/**
- *
- */
-public class ClientRequestHandlerTest {
-
- private final Logger _logger = LoggerFactory.getLogger(ClientRequestHandlerTest.class);
-
- ZMQ.Context context;
- ExecutorService serverThread;
- final String SERVER_ADDRESS = "localhost:5553";
-
- ClientRequestHandler handler;
-
- @Before
- public void setUp() throws Exception {
- context = ZMQ.context(1);
- serverThread = Executors.newCachedThreadPool();
- handler = new ClientRequestHandler(context);
- }
-
- @After
- public void tearDown() throws Exception {
- serverThread.shutdown();
- MessagingUtil.closeZmqContext(context);
- handler.close();
- }
-
- @Test
- public void handle_SingleRemote_ShouldReturnResponse() throws Exception {
- serverThread.execute(MessagingUtil.startReplyServer(context, SERVER_ADDRESS, 1));
- Message request = new Message();
- request.setRecipient(SERVER_ADDRESS);
- Message response = handleMessageWithTimeout(request);
- Assert.assertNotNull(response);
- //should be connected to only 1 remote server
- Assert.assertEquals(1, handler.getWorkerCount());
- Assert.assertEquals(response.getRecipient(), SERVER_ADDRESS);
- }
-
- // @Test
- public void handle_MultiRemote_ShouldReturnResponses() throws Exception {
- ExecutorService threadPool = Executors.newCachedThreadPool();
- final int port = 5555;
- String serverAddress = null;
- for (int i = 0; i < 5; i++) {
- serverAddress = "localhost:" + (port + i);
- serverThread.execute(MessagingUtil.startReplyServer(context, serverAddress, 1));
- threadPool.execute(createEmptyMessageTaskAndHandle(handler, serverAddress));
- }
- Thread.sleep(5000);//wait for all messages to get processed
- //should be connected to 5 remote server
- Assert.assertEquals(5, handler.getWorkerCount());
- }
-
- private Runnable createEmptyMessageTaskAndHandle(final ClientRequestHandler handler, final String serverAddress) {
-
- return new Runnable() {
- @Override
- public void run() {
- Message request = new Message();
- request.setRecipient(serverAddress);
- try {
- Message response = handleMessageWithTimeout(request);
- Assert.assertNotNull(response);
- Assert.assertEquals(response.getRecipient(), serverAddress);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- };
- }
-
- private Message handleMessageWithTimeout(final Message request) {
- Message response = null;
-
- FutureTask<?> task = new FutureTask<Message>(new Callable<Message>() {
-
- @Override
- public Message call() {
- try {
- return handler.handle(request);
- } catch (Exception e) {
- _logger.debug("Client handler failed to handle request. Exception is [{}]", e);
- }
- return null;
- }
- });
-
- serverThread.execute(task);
-
- try {
- response = (Message) task.get(5L, TimeUnit.SECONDS); //wait for max 5 sec for server to respond
- } catch (Exception e) {/*ignore and continue*/}
-
- return response;
- }
-
-}
+++ /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.connector.remoterpc;
-
-import java.util.HashSet;
-import java.util.Set;
-
-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;
-
-/**
- * Mock implementation of routing table
- */
-public class MockRoutingTable<K, V> implements RoutingTable {
-
-
- @Override
- public void addRoute(Object o, Object o2) throws RoutingTableException, SystemException {
-
- }
-
- @Override
- public void addGlobalRoute(Object o, Object o2) throws RoutingTableException, SystemException {
-
- }
-
- @Override
- public void removeRoute(Object o, Object o2) {
-
- }
-
- @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<String> getRoutes(Object o) {
- Set<String> routes = new HashSet<String>();
- routes.add("localhost:5554");
- return routes;
- }
-
- @Override
- public Object getLastAddedRoute(Object o) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
-// @Override
-// public Set<Map.Entry> getAllRoutes() {
-// return Collections.emptySet();
-// }
-
-// @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) 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.connector.remoterpc;
-
-import org.junit.Test;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeUtils;
-import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.StringWriter;
-
-public class SerilizationTest {
-
- private static final Logger _logger = LoggerFactory.getLogger(SerilizationTest.class);
-
- public void fromXml() {
- }
-
- @Test
- public void toXml() throws FileNotFoundException {
-
- //InputStream xmlStream = SerilizationTest.class.getResourceAsStream("/FourSimpleChildren.xml");
- InputStream xmlStream = SerilizationTest.class.getResourceAsStream("/AddFlow.xml");
- StringWriter writer = new StringWriter();
-
- CompositeNode data = loadCompositeNode(xmlStream);
- Document domTree = NodeUtils.buildShadowDomTree(data);
- try {
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- //transformer.setOutputProperty(OutputKeys.METHOD, "xml");
- //transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- //transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- //transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
- transformer.transform(new DOMSource(domTree), new StreamResult(writer));
- } catch (TransformerException e) {
- _logger.error("Error during translation of Document to OutputStream", e);
- }
-
- _logger.info("Parsed xml [{}]", writer.toString());
- }
-
- // Figure out how to include TestUtils through pom ...was getting errors
- private CompositeNode loadCompositeNode(InputStream xmlInputStream) throws FileNotFoundException {
- if (xmlInputStream == null) {
- throw new IllegalArgumentException();
- }
- Node<?> dataTree;
- try {
- dataTree = XmlTreeBuilder.buildDataTree(xmlInputStream);
- } catch (XMLStreamException e) {
- _logger.error("Error during building data tree from XML", e);
- return null;
- }
- if (dataTree == null) {
- _logger.error("data tree is null");
- return null;
- }
- if (dataTree instanceof SimpleNode) {
- _logger.error("RPC XML was resolved as SimpleNode");
- return null;
- }
- return (CompositeNode) dataTree;
- }
-}
+++ /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 static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-
-import junit.framework.Assert;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-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;
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.zeromq.ZMQ;
-
-import zmq.Ctx;
-import zmq.SocketBase;
-
-import com.google.common.base.Optional;
-
-public class ServerImplTest {
-
- private static ZMQ.Context context;
- private ServerImpl server;
- private Broker.ProviderSession brokerSession;
- private RoutingTableProvider routingTableProvider;
- private RpcRegistrationListener listener;
-
- ExecutorService pool;
-
- //Server configuration
- private final int HANDLER_COUNT = 2;
- private final int HWM = 200;
- private final int port = 5554;
- //server address
- private final String SERVER_ADDRESS = "tcp://localhost:5554";
-
- //@BeforeClass
- public static void init() {
- context = ZMQ.context(1);
- }
-
- //@AfterClass
- public static void destroy() {
- MessagingUtil.closeZmqContext(context);
- }
-
- @Before
- public void setup() throws InterruptedException {
- context = ZMQ.context(1);
- brokerSession = mock(Broker.ProviderSession.class);
- routingTableProvider = mock(RoutingTableProvider.class);
- listener = mock(RpcRegistrationListener.class);
-
- server = new ServerImpl(port);
- server.setBrokerSession(brokerSession);
-
- 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);
- when(brokerSession.getSupportedRpcs()).thenReturn(Collections.<QName>emptySet());
- when(brokerSession.rpc(null, mock(CompositeNode.class))).thenReturn(null);
- server.start();
- Thread.sleep(5000);//wait for server to start
- }
-
- @After
- public void tearDown() throws InterruptedException {
-
- if (pool != null)
- pool.shutdown();
-
- if (server != null)
- server.stop();
-
- MessagingUtil.closeZmqContext(context);
-
- Thread.sleep(5000);//wait for server to stop
- Assert.assertEquals(ServerImpl.State.STOPPED, server.getStatus());
- }
-
- @Test
- public void getBrokerSession_Call_ShouldReturnBrokerSession() throws Exception {
- Optional<Broker.ProviderSession> mayBeBroker = server.getBrokerSession();
-
- if (mayBeBroker.isPresent())
- Assert.assertEquals(brokerSession, mayBeBroker.get());
- else
- Assert.fail("Broker does not exist in Remote RPC Server");
-
- }
-
- @Test
- public void start_Call_ShouldSetServerStatusToStarted() throws Exception {
- Assert.assertEquals(ServerImpl.State.STARTED, server.getStatus());
-
- }
-
- @Test
- public void start_Call_ShouldCreateNZmqSockets() throws Exception {
- final int EXPECTED_COUNT = 2 + HANDLER_COUNT; //1 ROUTER + 1 DEALER + HANDLER_COUNT
-
- Optional<ZMQ.Context> mayBeContext = server.getZmqContext();
- if (mayBeContext.isPresent())
- Assert.assertEquals(EXPECTED_COUNT, findSocketCount(mayBeContext.get()));
- else
- Assert.fail("ZMQ Context does not exist in Remote RPC Server");
- }
-
- @Test
- public void start_Call_ShouldCreate1ServerThread() {
- final String SERVER_THREAD_NAME = "remote-rpc-server";
- final int EXPECTED_COUNT = 1;
- List<Thread> serverThreads = findThreadsWithName(SERVER_THREAD_NAME);
- Assert.assertEquals(EXPECTED_COUNT, serverThreads.size());
- }
-
- @Test
- public void start_Call_ShouldCreateNHandlerThreads() {
- //final String WORKER_THREAD_NAME = "remote-rpc-worker";
- final int EXPECTED_COUNT = HANDLER_COUNT;
-
- Optional<ServerRequestHandler> serverRequestHandlerOptional = server.getHandler();
- if (serverRequestHandlerOptional.isPresent()){
- ServerRequestHandler handler = serverRequestHandlerOptional.get();
- ThreadPoolExecutor workerPool = handler.getWorkerPool();
- Assert.assertEquals(EXPECTED_COUNT, workerPool.getPoolSize());
- } else {
- Assert.fail("Server is in illegal state. ServerHandler does not exist");
- }
-
- }
-
- @Test
- public void testStop() throws Exception {
-
- }
-
- @Test
- public void testOnRouteUpdated() throws Exception {
-
- }
-
- @Test
- public void testOnRouteDeleted() throws Exception {
-
- }
-
- private int findSocketCount(ZMQ.Context context)
- throws NoSuchFieldException, IllegalAccessException {
- Field ctxField = context.getClass().getDeclaredField("ctx");
- ctxField.setAccessible(true);
- Ctx ctx = Ctx.class.cast(ctxField.get(context));
-
- Field socketListField = ctx.getClass().getDeclaredField("sockets");
- socketListField.setAccessible(true);
- List<SocketBase> sockets = List.class.cast(socketListField.get(ctx));
-
- return sockets.size();
- }
-
- private List<Thread> findThreadsWithName(String name) {
- Thread[] threads = new Thread[Thread.activeCount()];
- Thread.enumerate(threads);
-
- List<Thread> foundThreads = new ArrayList<Thread>();
- for (Thread t : threads) {
- if (t.getName().startsWith(name))
- foundThreads.add(t);
- }
-
- return foundThreads;
- }
-}
+++ /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 static org.mockito.Mockito.mock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import junit.framework.Assert;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.sal.connector.remoterpc.utils.MessagingUtil;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.zeromq.ZMQ;
-
-public class ServerRequestHandlerTest {
-
- ServerRequestHandler handler;
- ZMQ.Context context;
- ExecutorService executorService = Executors.newCachedThreadPool();
- private final int workerCount = 2;
- private final String mockDealerAddress = "inproc://rpc-request-handler";
- private final String mockServerIp = "localhost";
- private final int mockServerPort = 5554;
-
- @Before
- public void setUp() throws Exception {
- context = ZMQ.context(1);
- String mockServerAddress = mockServerIp + ":" + mockServerPort;
- Broker.ProviderSession mockSession = mock(Broker.ProviderSession.class);
- handler = new ServerRequestHandler(context, mockSession, workerCount, mockDealerAddress, mockServerAddress);
- handler.start();
- }
-
- @After
- public void tearDown() throws Exception {
- executorService.shutdown();
- MessagingUtil.closeZmqContext(context);
- handler.close();
- }
-
- @Test
- public void testStart() throws Exception {
- //should start workers == workerCount
- Assert.assertEquals(workerCount, handler.getWorkerPool().getPoolSize());
-
- //killing a thread should recreate another one
-
- //start router-dealer bridge
- executorService.execute(MessagingUtil.createRouterDealerBridge(context, mockDealerAddress, mockServerPort));
- Thread.sleep(1000); //give sometime for socket initialization
-
- //this will kill the thread
- final String WORKER_THREAD_NAME = "remote-rpc-worker";
- interruptAThreadWithName(WORKER_THREAD_NAME);
-
- //send 4 message to router
- for (int i = 0; i < 4; i++)
- executorService.execute(MessagingUtil.sendAnEmptyMessage(context, "tcp://" + mockServerIp + ":" + mockServerPort));
-
- //worker pool size should not change.
- Assert.assertEquals(workerCount, handler.getWorkerPool().getPoolSize());
-
- Thread.sleep(10000); //wait for processing to complete
- }
-
- @Test
- public void testClose() throws Exception {
-
- }
-
- /**
- * Interrupts the first thread found whose name starts with the provided name
- *
- * @param name
- */
- private void interruptAThreadWithName(String name) {
- List<Thread> workerThreads = findThreadsWithName(name);
- if (workerThreads.size() > 0) workerThreads.get(0).interrupt();
- }
-
- /**
- * Find all threads that start with the given name
- *
- * @param name
- * @return
- */
- private List<Thread> findThreadsWithName(String name) {
- Thread[] threads = new Thread[Thread.activeCount()];
- Thread.enumerate(threads);
-
- List<Thread> foundThreads = new ArrayList<Thread>();
- for (Thread t : threads) {
- if (t.getName().startsWith(name))
- foundThreads.add(t);
- }
-
- return foundThreads;
- }
-}
+++ /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.utils;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-import junit.framework.Assert;
-
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-public class MessagingUtil {
-
- private static final Logger _logger = LoggerFactory.getLogger(MessagingUtil.class);
-
- public static Runnable startReplyServer(final ZMQ.Context context,
- final String serverAddress,
- final int numRequests /*number of requests after which server shuts down*/) {
- return new Runnable() {
-
- @Override
- public void run() {
- final ZMQ.Socket socket = context.socket(ZMQ.REP);
- try {
- int returnCode = socket.bind("tcp://" + serverAddress);
- Assert.assertNotSame(-1, returnCode);
- _logger.info(" Starting reply server[{}] for test...", serverAddress);
-
- //for (int i=0;i<numRequests;i++) {
- while (!Thread.currentThread().isInterrupted()) {
- byte[] bytes = socket.recv();
- _logger.debug(" Got request ");
- socket.send(bytes);
- _logger.debug(" Sent response ");
- }
- } catch (Exception x) {
- StringWriter w = new StringWriter();
- PrintWriter p = new PrintWriter(w);
- x.printStackTrace(p);
- _logger.debug(w.toString());
- } finally {
- socket.close();
- _logger.info("Shutting down reply server");
- }
- }
- };
- }
-
- public static Runnable createRouterDealerBridge(final ZMQ.Context context, final String dealerAddress, final int routerPort) {
- return new Runnable() {
- @Override
- public void run() {
- ZMQ.Socket router = null;
- ZMQ.Socket dealer = null;
- try {
- router = context.socket(ZMQ.ROUTER);
- dealer = context.socket(ZMQ.DEALER);
- router.bind("tcp://*:" + routerPort);
- dealer.bind(dealerAddress);
- ZMQ.proxy(router, dealer, null);
- } catch (Exception e) {/*Ignore*/} finally {
- if (router != null) router.close();
- if (dealer != null) dealer.close();
- }
- }
- };
- }
-
- public static Runnable sendAMessage(final ZMQ.Context context, final String serverAddress, final Message msg)
- throws IOException, ClassNotFoundException, InterruptedException {
-
- return new Runnable() {
- @Override
- public void run() {
- final ZMQ.Socket socket = context.socket(ZMQ.REQ);
- try {
-
- socket.connect(serverAddress);
- System.out.println(Thread.currentThread().getName() + " Sending message");
- try {
- socket.send(Message.serialize(msg));
- } catch (IOException e) {
- e.printStackTrace();
- }
- byte[] bytes = socket.recv();
- Message response = null;
- try {
- response = (Message) Message.deserialize(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + " Got response " + response);
- } catch (Exception x) {
- x.printStackTrace();
- } finally {
- socket.close();
- }
- }
- };
- }
-
- public static Runnable sendAnEmptyMessage(final ZMQ.Context context, final String serverAddress)
- throws IOException, ClassNotFoundException, InterruptedException {
-
- return new Runnable() {
- @Override
- public void run() {
- final ZMQ.Socket socket = context.socket(ZMQ.REQ);
- try {
-
- socket.connect(serverAddress);
- System.out.println(Thread.currentThread().getName() + " Sending message");
- try {
- socket.send(Message.serialize(new Message()));
- } catch (IOException e) {
- e.printStackTrace();
- }
- byte[] bytes = socket.recv();
- Message response = null;
- try {
- response = (Message) Message.deserialize(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + " Got response " + response);
- } catch (Exception x) {
- x.printStackTrace();
- } finally {
- socket.close();
- }
- }
- };
- }
-
- public static Message createEmptyMessage() {
- return new Message();
- }
-
- /**
- * Closes ZMQ Context. It tries to gracefully terminate the context. If
- * termination takes more than a second, its forcefully shutdown.
- */
- public static void closeZmqContext(final ZMQ.Context context) {
- if (context == null) return;
-
- ExecutorService exec = Executors.newSingleThreadExecutor();
- FutureTask<?> zmqTermination = new FutureTask<Void>(new Runnable() {
-
- @Override
- public void run() {
- try {
- if (context != null)
- context.term();
- _logger.debug("ZMQ Context terminated gracefully!");
- } catch (Exception e) {/*Ignore and continue shutdown*/}
- }
- }, null);
-
- exec.execute(zmqTermination);
-
- try {
- zmqTermination.get(1L, TimeUnit.SECONDS);
- } catch (Exception e) {
- _logger.debug("ZMQ Context terminated forcefully!");
- }
-
- exec.shutdownNow();
- }
-}
+++ /dev/null
-package org.opendaylight.controller.sal.connector.remoterpc.utils;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.zeromq.ZMQ;
-
-public class RemoteServerTestClient {
-
-
-
- public static void main(String args[]) throws Exception{
- String serverAddress = "tcp://10.195.128.108:5666";
- ZMQ.Context ctx = ZMQ.context(1);
- ExecutorService executor = Executors.newSingleThreadExecutor();
- RemoteServerTestClient client = new RemoteServerTestClient();
- executor.execute(
- MessagingUtil.sendAMessage(ctx, serverAddress, client.createPingMessage(serverAddress))
- );
- MessagingUtil.sendAMessage(ctx, serverAddress, client.createPingMessage(serverAddress));
-
- Thread.sleep(5000);
- MessagingUtil.closeZmqContext(ctx);
- executor.shutdown();
- }
-
- public Message createPingMessage(String serverAddress){
- Message ping = new Message.MessageBuilder()
- .type(Message.MessageType.PING)
- .sender("localhost:5444")
- .recipient(serverAddress)
- .build();
-
- return ping;
- }
- public Message createAddFlowMessage(String serverAddress ){
-
- RpcRouter.RouteIdentifier<?, ?, ?> routeIdentifier = getAddFlowRpcIdentifier();
-
- Message addFlow = new Message.MessageBuilder()
- .type(Message.MessageType.REQUEST)
- .sender("localhost:5444")
- .recipient(serverAddress)
- .route(routeIdentifier)
- .payload(getAddFlowPayload(1,1))
- .build();
-
- return addFlow;
- }
-
- private RpcRouter.RouteIdentifier<?, ?, ?> getAddFlowRpcIdentifier(){
- throw new UnsupportedOperationException();
- }
-
- private CompositeNode getAddFlowPayload(int flowId, int tableId){
- final String xml =
- "<flow xmlns=\"urn:opendaylight:flow:inventory\">"
- + "<priority>5</priority>"
- + "<flow-name>Foo</flow-name>"
- + "<match>"
- + "<ethernet-match>"
- + "<ethernet-type>"
- + "<type>2048</type>"
- + "</ethernet-type>"
- + "</ethernet-match>"
- + "<ipv4-destination>10.0.10.2/24</ipv4-destination>"
- + "</match>"
- + "<id>" + flowId + "</id>"
- + "<table_id>" + tableId + "</table_id>"
- + "<instructions>"
- + "<instruction>"
- + "<order>0</order>"
- + "<apply-actions>"
- + "<action>"
- + "<order>0</order>"
- + "<dec-nw-ttl/>"
- + "</action>"
- + "</apply-actions>"
- + "</instruction>"
- + "</instructions>"
- + "</flow>";
-
- return XmlUtils.xmlToCompositeNode(xml);
- }
-}
+++ /dev/null
-<add-flow xmlns="urn:opendaylight:flow:service">
- <input>
- <transaction-uri>BA-7</transaction-uri>
- <table_id>4</table_id>
- <priority>5</priority>
- <node>
- /(urn:opendaylight:inventory?revision=2013-08-19)nodes/(urn:opendaylight:inventory?revision=2013-08-19)node[{(urn:opendaylight:inventory?revision=2013-08-19)id=openflow:1}]
- </node>
- <match>
- <ipv4-destination>10.0.10.2/24</ipv4-destination>
- <ethernet-match>
- <ethernet-type>
- <type>2048</type>
- </ethernet-type>
- </ethernet-match>
- </match>
- <instructions>
- <instruction>
- <order>0</order>
- <apply-actions>
- <action>
- <order>0</order>
- <dec-nw-ttl/>
- </action>
- </apply-actions>
- </instruction>
- </instructions>
- <flow-table>
- /(urn:opendaylight:inventory?revision=2013-08-19)nodes/(urn:opendaylight:inventory?revision=2013-08-19)node[{(urn:opendaylight:inventory?revision=2013-08-19)id=openflow:1}]/(urn:opendaylight:flow:inventory?revision=2013-08-19)table[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=4}]
- </flow-table>
- <flow-ref>
- /(urn:opendaylight:inventory?revision=2013-08-19)nodes/(urn:opendaylight:inventory?revision=2013-08-19)node[{(urn:opendaylight:inventory?revision=2013-08-19)id=openflow:1}]/(urn:opendaylight:flow:inventory?revision=2013-08-19)table[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=4}]/(urn:opendaylight:flow:inventory?revision=2013-08-19)flow[{(urn:opendaylight:flow:inventory?revision=2013-08-19)id=4}]
- </flow-ref>
- <flow-name>Foo</flow-name>
- </input>
-</add-flow>
\ No newline at end of file
+++ /dev/null
-<rpc>
- <name>eth0</name>
- <type>ethernetCsmacd</type>
- <enabled>false</enabled>
- <description>some interface</description>
-</rpc>
+++ /dev/null
-<configuration scan="true">
-
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
- </pattern>
- </encoder>
- </appender>
-
- <root level="DEBUG">
- <appender-ref ref="STDOUT" />
- </root>
-</configuration>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-parent</artifactId>
- <version>1.1-SNAPSHOT</version>
- </parent>
- <artifactId>sal-remoterpc-connector-test-consumer</artifactId>
- <packaging>bundle</packaging>
-
- <dependencies>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-impl</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Bundle-Activator>org.opendaylight.controller.sample.zeromq.consumer.ExampleConsumer</Bundle-Activator>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
- </scm>
-</project>
+++ /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.zeromq.consumer;
-
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.Hashtable;
-import java.util.concurrent.Future;
-
-import org.opendaylight.controller.sal.core.api.AbstractConsumer;
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
-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.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
-
-import javax.xml.stream.XMLStreamException;
-
-public class ExampleConsumer extends AbstractConsumer {
-
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
-
- private ConsumerSession session;
-
- private ServiceRegistration<ExampleConsumer> thisReg;
- private Logger _logger = LoggerFactory.getLogger(ExampleConsumer.class);
-
- @Override
- public void onSessionInitiated(ConsumerSession session) {
- this.session = session;
- }
-
- public RpcResult<CompositeNode> invokeRpc(QName qname, CompositeNode input) {
- _logger.info("Invoking RPC:[{}] with Input:[{}]", qname.getLocalName(), input);
- RpcResult<CompositeNode> result = null;
- Future<RpcResult<CompositeNode>> future = ExampleConsumer.this.session.rpc(qname, input);
- try {
- result = future.get();
- } catch (Exception e) {
- e.printStackTrace();
- }
- _logger.info("Returning Result:[{}]", result);
- return result;
- }
-
- @Override
- protected void startImpl(BundleContext context){
- thisReg = context.registerService(ExampleConsumer.class, this, new Hashtable<String,String>());
- }
- @Override
- protected void stopImpl(BundleContext context) {
- super.stopImpl(context);
- thisReg.unregister();
- }
-
- public CompositeNode getValidCompositeNodeWithOneSimpleChild() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/OneSimpleChild.xml");
- return loadCompositeNode(xmlStream);
- }
-
- public CompositeNode getValidCompositeNodeWithTwoSimpleChildren() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/TwoSimpleChildren.xml");
- return loadCompositeNode(xmlStream);
- }
-
- public CompositeNode getValidCompositeNodeWithFourSimpleChildren() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/FourSimpleChildren.xml");
- return loadCompositeNode(xmlStream);
- }
-
- public CompositeNode getValidCompositeNodeWithOneSimpleOneCompositeChild() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/OneSimpleOneCompositeChild.xml");
- return loadCompositeNode(xmlStream);
- }
-
- public CompositeNode getValidCompositeNodeWithTwoCompositeChildren() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/TwoCompositeChildren.xml");
- return loadCompositeNode(xmlStream);
- }
-
- public CompositeNode getInvalidCompositeNodeSimpleChild() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/InvalidSimpleChild.xml");
- return loadCompositeNode(xmlStream);
- }
-
- public CompositeNode getInvalidCompositeNodeCompositeChild() throws FileNotFoundException {
- InputStream xmlStream = ExampleConsumer.class.getResourceAsStream("/InvalidCompositeChild.xml");
- return loadCompositeNode(xmlStream);
- }
-
- //Note to self: Stolen from TestUtils
- ///Users/alefan/odl/controller4/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java
- // Figure out how to include TestUtils through pom ...was getting errors
- private CompositeNode loadCompositeNode(InputStream xmlInputStream) throws FileNotFoundException {
- if (xmlInputStream == null) {
- throw new IllegalArgumentException();
- }
- Node<?> dataTree;
- try {
- dataTree = XmlTreeBuilder.buildDataTree(xmlInputStream);
- } catch (XMLStreamException e) {
- _logger.error("Error during building data tree from XML", e);
- return null;
- }
- if (dataTree == null) {
- _logger.error("data tree is null");
- return null;
- }
- if (dataTree instanceof SimpleNode) {
- _logger.error("RPC XML was resolved as SimpleNode");
- return null;
- }
- return (CompositeNode) dataTree;
- }
-}
+++ /dev/null
-<rpc>
- <name>eth0</name>
- <type>ethernetCsmacd</type>
- <enabled>false</enabled>
- <description>some interface</description>
-</rpc>
+++ /dev/null
-<rpc>
- <innerinterface1>
- <name>eth1</name>
- <type>ethernet</type>
- <enabled>false</enabled>
- <description>some interface</description>
- </innerinterface1>
- <innerinterface2>
- <name>error</name>
- <type>ethernet</type>
- <enabled>true</enabled>
- <description>some interface</description>
- </innerinterface2>
-</rpc>
+++ /dev/null
-<rpc>
- <name>error</name>
-</rpc>
+++ /dev/null
-<rpc>
- <name>eth0</name>
-</rpc>
+++ /dev/null
-<rpc>
- <name>eth0</name>
- <innerinterface>
- <name>eth1</name>
- <type>ethernetCsmacd</type>
- <enabled>false</enabled>
- <description>some interface</description>
- </innerinterface>
-</rpc>
+++ /dev/null
-<rpc>
- <innerinterface1>
- <name>eth1</name>
- <type>ethernet</type>
- <enabled>false</enabled>
- <description>some interface</description>
- </innerinterface1>
- <innerinterface2>
- <name>eth2</name>
- <type>ethernet</type>
- <enabled>true</enabled>
- <description>some interface</description>
- </innerinterface2>
-</rpc>
+++ /dev/null
-<rpc>
- <name>eth0</name>
- <type>ethernetCsmacd</type>
-</rpc>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-parent</artifactId>
- <version>1.1-SNAPSHOT</version>
- <relativePath>../..</relativePath>
- </parent>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-parent</artifactId>
- <packaging>pom</packaging>
-
- <modules>
- <module>consumer-service</module>
- <module>provider-service</module>
- <module>test-it</module>
- <module>test-nb</module>
- </modules>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
- </scm>
-
-</project>
+++ /dev/null
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>sal-remoterpc-connector-test-parent</artifactId>
- <groupId>org.opendaylight.controller.tests</groupId>
- <version>1.1-SNAPSHOT</version>
- </parent>
- <artifactId>sal-remoterpc-connector-test-provider</artifactId>
- <packaging>bundle</packaging>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
- </scm>
-
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Bundle-Activator>org.opendaylight.controller.sample.zeromq.provider.ExampleProvider</Bundle-Activator>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-api</artifactId>
- </dependency>
-
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remoterpc-connector</artifactId>
- </dependency>
-
- </dependencies>
-</project>
+++ /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.zeromq.provider;
-
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.common.util.Rpcs;
-import org.opendaylight.controller.sal.core.api.AbstractProvider;
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ExampleProvider extends AbstractProvider implements RpcImplementation {
-
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
- private RpcRegistration reg;
-
- private ServiceRegistration thisReg;
-
- private ProviderSession session;
- private Logger _logger = LoggerFactory.getLogger(ExampleProvider.class);
-
- @Override
- public void onSessionInitiated(ProviderSession session) {
- this.session = session;
- }
-
- @Override
- public Set<QName> getSupportedRpcs() {
- Set<QName> supportedRpcs = new HashSet<QName>();
- supportedRpcs.add(QNAME);
- return supportedRpcs;
- }
-
- @Override
- public RpcResult<CompositeNode> invokeRpc(final QName rpc, CompositeNode input) {
- boolean success = false;
- CompositeNode output = null;
- Collection<RpcError> errors = new ArrayList<>();
-
- // Only handle supported RPC calls
- if (getSupportedRpcs().contains(rpc)) {
- if (input == null) {
- errors.add(RpcErrors.getRpcError("app", "tag", "info", RpcError.ErrorSeverity.WARNING, "message:null input", RpcError.ErrorType.RPC, null));
- }
- else {
- if (isErroneousInput(input)) {
- errors.add(RpcErrors.getRpcError("app", "tag", "info", RpcError.ErrorSeverity.ERROR, "message:error", RpcError.ErrorType.RPC, null));
- }
- else {
- success = true;
- output = addSuccessNode(input);
- }
- }
- }
- return Rpcs.getRpcResult(success, output, errors);
- }
-
- // Examines input -- dives into CompositeNodes and finds any value equal to "error"
- private boolean isErroneousInput(CompositeNode input) {
- for (Node<?> n : input.getChildren()) {
- if (n instanceof CompositeNode) {
- if (isErroneousInput((CompositeNode)n)) {
- return true;
- }
- }
- else { //SimpleNode
- if ((input.getChildren().get(0).getValue()).equals("error")) {
- return true;
- }
- }
- }
- return false;
- }
-
- // Adds a child SimpleNode containing the value "success" to the input CompositeNode
- private CompositeNode addSuccessNode(CompositeNode input) {
- List<Node<?>> list = new ArrayList<Node<?>>(input.getChildren());
- SimpleNodeTOImpl<String> simpleNode = new SimpleNodeTOImpl<String>(QNAME, input, "success");
- list.add(simpleNode);
- return new CompositeNodeTOImpl(QNAME, null, list);
- }
-
- @Override
- protected void startImpl(BundleContext context) {
- thisReg = context.registerService(ExampleProvider.class, this, new Hashtable<String, String>());
- }
-
- @Override
- protected void stopImpl(BundleContext context) {
- if (reg != null) {
- try {
- reg.close();
- thisReg.unregister();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
-
- public void announce(QName name) {
- _logger.debug("Announcing [{}]\n\n\n", name);
- reg = this.session.addRpcImplementation(name, this);
- }
-
-}
+++ /dev/null
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>sal-remoterpc-connector-test-parent</artifactId>
- <groupId>org.opendaylight.controller.tests</groupId>
- <version>1.1-SNAPSHOT</version>
- </parent>
- <artifactId>sal-remoterpc-connector-test-it</artifactId>
- <scm>
- <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
- <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
- <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
- </scm>
-
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>maven-paxexam-plugin</artifactId>
- <executions>
- <execution>
- <id>generate-config</id>
- <goals>
- <goal>generate-depends-file</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>${lifecycle.mapping.version}</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>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-provider</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-consumer</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-broker-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-native</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-link-mvn</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.url</groupId>
- <artifactId>pax-url-aether</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remoterpc-connector</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- </exclusion>
- <exclusion>
- <artifactId>commons-io</artifactId>
- <groupId>commons-io</groupId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-data-api</artifactId>
- </dependency>
- <!--dependency> <groupId>org.opendaylight.yangtools</groupId> <artifactId>yang-data-impl</artifactId>
- <version>${yangtools.version}</version> </dependency -->
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-impl</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>antlr4-runtime-osgi-nohead</artifactId>
- </dependency>
-
- <!-- routing table dependencies -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>zeromq-routingtable.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal.implementation</artifactId>
- <exclusions>
- <exclusion>
- <artifactId>commons-io</artifactId>
- <groupId>commons-io</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- </exclusion>
- <exclusion>
- <artifactId>commons-io</artifactId>
- <groupId>commons-io</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager.it.implementation</artifactId>
- <exclusions>
- <exclusion>
- <artifactId>commons-io</artifactId>
- <groupId>commons-io</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.stub</artifactId>
- <exclusions>
- <exclusion>
- <artifactId>commons-io</artifactId>
- <groupId>commons-io</groupId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>eclipselink</groupId>
- <artifactId>javax.resource</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>ietf-netconf-monitoring</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-binding</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>yang-ext</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.model</groupId>
- <artifactId>opendaylight-l2-types</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-it</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-config</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-broker-impl</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-inventory</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-common</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-connector-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>clustering.services</artifactId>
- </dependency>
-
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>${jackson.version}</version>
- </dependency>
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- <version>${jackson.version}</version>
- </dependency>
-
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- <version>${jackson.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.zeromq</groupId>
- <artifactId>jeromq</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>xtend-lib-osgi</artifactId>
- <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>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-netconf-connector</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>logback-config</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-persister-file-xml-adapter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-impl</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-client</artifactId>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam</artifactId>
- <version>${exam.version}</version>
- <!-- Compile scope here is intentional, it is used in TestHelper
- class which could be downloaded via nexus and reused in other integration
- tests. -->
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-link-mvn</artifactId>
- <version>${exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>equinoxSDK381</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>log4j-over-slf4j</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-service</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>config-manager</artifactId>
- <exclusions>
- <exclusion>
- <artifactId>commons-io</artifactId>
- <groupId>commons-io</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.model</groupId>
- <artifactId>model-flow-management</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools.thirdparty</groupId>
- <artifactId>antlr4-runtime-osgi-nohead</artifactId>
- </dependency>
- </dependencies>
-</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.sample.zeromq.test.it;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.opendaylight.controller.sal.connector.remoterpc.RemoteRpcClient;
-
-import org.opendaylight.controller.sal.connector.remoterpc.dto.Message;
-import org.opendaylight.controller.sal.core.api.Broker;
-import org.opendaylight.controller.sample.zeromq.consumer.ExampleConsumer;
-import org.opendaylight.controller.sample.zeromq.provider.ExampleProvider;
-
-import org.opendaylight.controller.test.sal.binding.it.TestHelper;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.ops4j.pax.exam.Configuration;
-import org.ops4j.pax.exam.Option;
-import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.util.Filter;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.ServiceReference;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.zeromq.ZMQ;
-
-import javax.inject.Inject;
-
-import java.io.IOException;
-import java.net.URI;
-
-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.ops4j.pax.exam.CoreOptions.*;
-
-@RunWith(PaxExam.class)
-public class RouterTest {
-
- private Logger _logger = LoggerFactory.getLogger(RouterTest.class);
-
- public static final String ODL = "org.opendaylight.controller";
- public static final String YANG = "org.opendaylight.yangtools";
- public static final String SAMPLE = "org.opendaylight.controller.tests";
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
-
-
- @Inject
- org.osgi.framework.BundleContext ctx;
-
- @Inject
- @Filter(timeout=60*1000)
- Broker broker;
-
- private ZMQ.Context zmqCtx = ZMQ.context(1);
- //private Server router;
- //private ExampleProvider provider;
-
- //@Test
- public void testInvokeRpc() throws Exception{
- //Thread.sleep(1000);
- //Send announcement
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
-
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
-
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
-
- _logger.debug("Provider sends announcement [{}]", "heartbeat");
- provider.announce(QNAME);
- ServiceReference routerRef = ctx.getServiceReference(RemoteRpcClient.class);
- RemoteRpcClient router = (RemoteRpcClient) ctx.getService(routerRef);
- _logger.debug("Found router[{}]", router);
- _logger.debug("Invoking RPC [{}]", QNAME);
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = router.invokeRpc(QNAME, consumer.getValidCompositeNodeWithOneSimpleChild());
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertNotNull(result);
- }
- }
-
- @Test
- public void testInvokeRpcWithValidSimpleNode() throws Exception{
- //Thread.sleep(1500);
-
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
- // Provider sends announcement
- _logger.debug("Provider sends announcement [{}]", "heartbeat");
- provider.announce(QNAME);
- // Consumer invokes RPC
- _logger.debug("Invoking RPC [{}]", QNAME);
- CompositeNode input = consumer.getValidCompositeNodeWithOneSimpleChild();
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, input);
- Assert.assertNotNull(result);
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertTrue(result.isSuccessful());
- Assert.assertNotNull(result.getResult());
- Assert.assertEquals(0, result.getErrors().size());
- Assert.assertEquals(input.getChildren().size()+1, result.getResult().getChildren().size());
- }
- }
-
- @Test
- public void testInvokeRpcWithValidSimpleNodes() throws Exception{
- //Thread.sleep(1500);
-
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
- // Provider sends announcement
- _logger.debug("Provider sends announcement [{}]", "heartbeat");
- provider.announce(QNAME);
- // Consumer invokes RPC
- _logger.debug("Invoking RPC [{}]", QNAME);
- CompositeNode input = consumer.getValidCompositeNodeWithFourSimpleChildren();
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, input);
- Assert.assertNotNull(result);
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertTrue(result.isSuccessful());
- Assert.assertNotNull(result.getResult());
- Assert.assertEquals(0, result.getErrors().size());
- Assert.assertEquals(input.getChildren().size()+1, result.getResult().getChildren().size());
- }
- }
-
- @Test
- public void testInvokeRpcWithValidCompositeNode() throws Exception{
- //Thread.sleep(1500);
-
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
- // Provider sends announcement
- _logger.debug("Provider sends announcement [{}]", "heartbeat");
- provider.announce(QNAME);
- // Consumer invokes RPC
- _logger.debug("Invoking RPC [{}]", QNAME);
- CompositeNode input = consumer.getValidCompositeNodeWithTwoCompositeChildren();
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, input);
- Assert.assertNotNull(result);
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertTrue(result.isSuccessful());
- Assert.assertNotNull(result.getResult());
- Assert.assertEquals(0, result.getErrors().size());
- Assert.assertEquals(input.getChildren().size()+1, result.getResult().getChildren().size());
- }
- }
-
- @Test
- public void testInvokeRpcWithNullInput() throws Exception{
- //Thread.sleep(1500);
-
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
- // Provider sends announcement
- _logger.debug("Provider sends announcement [{}]", QNAME.getLocalName());
- provider.announce(QNAME);
- // Consumer invokes RPC
- _logger.debug("Invoking RPC [{}]", QNAME);
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, null);
- Assert.assertNotNull(result);
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertFalse(result.isSuccessful());
- Assert.assertNull(result.getResult());
- Assert.assertEquals(1, result.getErrors().size());
- Assert.assertEquals(RpcError.ErrorSeverity.WARNING, ((RpcError)result.getErrors().toArray()[0]).getSeverity());
- }
- }
-
- @Test
- public void testInvokeRpcWithInvalidSimpleNode() throws Exception{
- //Thread.sleep(1500);
-
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
- // Provider sends announcement
- _logger.debug("Provider sends announcement [{}]", QNAME.getLocalName());
- provider.announce(QNAME);
- // Consumer invokes RPC
- _logger.debug("Invoking RPC [{}]", QNAME);
- CompositeNode input = consumer.getInvalidCompositeNodeSimpleChild();
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, input);
- Assert.assertNotNull(result);
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertFalse(result.isSuccessful());
- Assert.assertNull(result.getResult());
- Assert.assertEquals(1, result.getErrors().size());
- Assert.assertEquals(RpcError.ErrorSeverity.ERROR, ((RpcError)result.getErrors().toArray()[0]).getSeverity());
- }
- }
-
- @Test
- public void testInvokeRpcWithInvalidCompositeNode() throws Exception{
- //Thread.sleep(1500);
-
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- Assert.assertNotNull(providerRef);
- ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
- Assert.assertNotNull(provider);
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- Assert.assertNotNull(consumerRef);
- ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
- Assert.assertNotNull(consumer);
-
- // Provider sends announcement
- _logger.debug("Provider sends announcement [{}]", QNAME.getLocalName());
- provider.announce(QNAME);
- // Consumer invokes RPC
- _logger.debug("Invoking RPC [{}]", QNAME);
- CompositeNode input = consumer.getInvalidCompositeNodeCompositeChild();
- for (int i = 0; i < 3; i++) {
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, input);
- Assert.assertNotNull(result);
- _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
- Assert.assertFalse(result.isSuccessful());
- Assert.assertNull(result.getResult());
- Assert.assertEquals(1, result.getErrors().size());
- Assert.assertEquals(RpcError.ErrorSeverity.ERROR, ((RpcError)result.getErrors().toArray()[0]).getSeverity());
- }
- }
-
- //@Test
- // This method is UNTESTED -- need to get around the bundling issues before I know if this even work
-// public void testInvokeRpcWithValidCompositeNode() throws Exception{
-// Thread.sleep(10000);
-// //Send announcement
-// ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
-// Assert.assertNotNull(providerRef);
-//
-// ExampleProvider provider = (ExampleProvider)ctx.getService(providerRef);
-// Assert.assertNotNull(provider);
-//
-// ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
-// Assert.assertNotNull(consumerRef);
-//
-// ExampleConsumer consumer = (ExampleConsumer)ctx.getService(consumerRef);
-// Assert.assertNotNull(consumer);
-//
-// _logger.debug("Provider sends announcement [{}]", "heartbeat");
-// provider.announce(QNAME);
-// ServiceReference routerRef = ctx.getServiceReference(Client.class);
-// Client router = (Client) ctx.getService(routerRef);
-// _logger.debug("Found router[{}]", router);
-// _logger.debug("Invoking RPC [{}]", QNAME);
-// for (int i = 0; i < 3; i++) {
-// RpcResult<CompositeNode> result = router.getInstance().invokeRpc(QNAME, consumer.getValidCompositeNodeWithOneSimpleChild());
-// _logger.debug("{}-> Result is: Successful:[{}], Payload:[{}], Errors: [{}]", i, result.isSuccessful(), result.getResult(), result.getErrors());
-// Assert.assertNotNull(result);
-// }
-// }
-
- private Message send(Message msg) throws IOException {
- ZMQ.Socket reqSocket = zmqCtx.socket(ZMQ.REQ);
- reqSocket.connect("tcp://localhost:5555");
- reqSocket.send(Message.serialize(msg));
- Message response = parseMessage(reqSocket);
-
- return response;
- }
-
- /**
- * @param socket
- * @return
- */
- private Message parseMessage(ZMQ.Socket socket) {
-
- Message msg = null;
- try {
- byte[] bytes = socket.recv();
- _logger.debug("Received bytes:[{}]", bytes.length);
- msg = (Message) Message.deserialize(bytes);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- return msg;
- }
-
-
- private void printState(){
- Bundle[] b = ctx.getBundles();
- _logger.debug("\n\nNumber of bundles [{}]\n\n]", b.length);
- for (int i=0;i<b.length;i++){
- _logger.debug("Bundle States {}-{} ",b[i].getSymbolicName(), stateToString(b[i].getState()));
-
- if ( Bundle.INSTALLED == b[i].getState() || (Bundle.RESOLVED == b[i].getState())){
- try {
- b[i].start();
- } catch (BundleException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- private String stateToString(int state) {
- switch (state) {
- case Bundle.ACTIVE:
- return "ACTIVE";
- case Bundle.INSTALLED:
- return "INSTALLED";
- case Bundle.RESOLVED:
- return "RESOLVED";
- case Bundle.UNINSTALLED:
- return "UNINSTALLED";
- default:
- return "Not CONVERTED";
- }
- }
-
- @Configuration
- public Option[] config() {
- return options(systemProperty("osgi.console").value("2401"),
- systemProperty("rpc.port").value("5555"),
- 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-binding-broker-impl").versionAsInProject().update(), //
- 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, "sal-core-api").versionAsInProject().update(), //
- mavenBundle(ODL, "sal-broker-impl").versionAsInProject(), //
- mavenBundle(ODL, "sal-core-spi").versionAsInProject().update(), //
- mavenBundle(ODL, "sal-connector-api").versionAsInProject(), //
-
-
- baseModelBundles(),
- bindingAwareSalBundles(),
- TestHelper.bindingIndependentSalBundles(),
- TestHelper.configMinumumBundles(),
- TestHelper.mdSalCoreBundles(),
-
- //Added the consumer
- mavenBundle(SAMPLE, "sal-remoterpc-connector-test-consumer").versionAsInProject(), //
- //**** These two bundles below are NOT successfully resolved -- some of their dependencies must be missing
- //**** This causes the "Message" error to occur, the class cannot be found
- mavenBundle(SAMPLE, "sal-remoterpc-connector-test-provider").versionAsInProject(), //
- mavenBundle(ODL, "sal-remoterpc-connector").versionAsInProject(), //
-
- mavenBundle(ODL, "zeromq-routingtable.implementation").versionAsInProject(),
- mavenBundle(YANG, "concepts").versionAsInProject(),
- mavenBundle(YANG, "yang-binding").versionAsInProject(), //
- mavenBundle(YANG, "yang-common").versionAsInProject(), //
- mavenBundle(YANG, "yang-data-api").versionAsInProject(), //
- mavenBundle(YANG, "yang-data-impl").versionAsInProject(), //
- mavenBundle(YANG, "yang-model-api").versionAsInProject(), //
- mavenBundle(YANG, "yang-parser-api").versionAsInProject(), //
- mavenBundle(YANG, "yang-parser-impl").versionAsInProject(), //
- mavenBundle(YANG, "yang-model-util").versionAsInProject(), //
- mavenBundle(YANG + ".thirdparty", "xtend-lib-osgi").versionAsInProject(), //
- mavenBundle(YANG + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), //
- mavenBundle("com.google.guava", "guava").versionAsInProject(), //
- mavenBundle("org.zeromq", "jeromq").versionAsInProject(),
- mavenBundle("org.codehaus.jackson", "jackson-mapper-asl").versionAsInProject(),
- mavenBundle("org.codehaus.jackson", "jackson-core-asl").versionAsInProject(),
- //routingtable dependencies
- systemPackages("sun.reflect", "sun.reflect.misc", "sun.misc"),
- // List framework bundles
- mavenBundle("equinoxSDK381", "org.eclipse.equinox.console").versionAsInProject(),
- mavenBundle("equinoxSDK381", "org.eclipse.equinox.util").versionAsInProject(),
- mavenBundle("equinoxSDK381", "org.eclipse.osgi.services").versionAsInProject(),
- mavenBundle("equinoxSDK381", "org.eclipse.equinox.ds").versionAsInProject(),
- mavenBundle("equinoxSDK381", "org.apache.felix.gogo.command").versionAsInProject(),
- mavenBundle("equinoxSDK381", "org.apache.felix.gogo.runtime").versionAsInProject(),
- mavenBundle("equinoxSDK381", "org.apache.felix.gogo.shell").versionAsInProject(),
- // List logger bundles
-
- mavenBundle("org.opendaylight.controller", "clustering.services")
- .versionAsInProject(),
- mavenBundle("org.opendaylight.controller", "clustering.stub")
- .versionAsInProject(),
-
-
- // List all the bundles on which the test case depends
- mavenBundle("org.opendaylight.controller", "sal")
- .versionAsInProject(),
- mavenBundle("org.opendaylight.controller", "sal.implementation")
- .versionAsInProject(),
- mavenBundle("org.jboss.spec.javax.transaction",
- "jboss-transaction-api_1.1_spec").versionAsInProject(),
- mavenBundle("org.apache.commons", "commons-lang3")
- .versionAsInProject(),
- mavenBundle("org.apache.felix",
- "org.apache.felix.dependencymanager")
- .versionAsInProject(),
-
- junitBundles()
- );
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<persisted-snapshots>
- <snapshots>
- <snapshot>
- <required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
- </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:md:sal:remote:rpc?module=odl-sal-dom-rpc-remote-cfg&revision=2013-10-28
- </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: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>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
- prefix:remote-zeromq-rpc-server
- </type>
- <name>remoter</name>
- <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">5666</port>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
- <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>
- </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-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>
+++ /dev/null
-<configuration scan="true">
-
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
- </pattern>
- </encoder>
- </appender>
-
-
- <logger name="org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort" level="ERROR"/>
-
- <root level="info">
- <appender-ref ref="STDOUT" />
- </root>
-</configuration>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>sal-remoterpc-connector-test-parent</artifactId>
- <groupId>org.opendaylight.controller.tests</groupId>
- <version>1.1-SNAPSHOT</version>
- </parent>
-
- <artifactId>sal-remoterpc-connector-test-nb</artifactId>
- <packaging>bundle</packaging>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <version>${bundle.plugin.version}</version>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Export-Package>
- </Export-Package>
- <Import-Package>
- com.sun.jersey.spi.container.servlet,
- !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.opendaylight.controller.sample.zeromq.provider,
- org.opendaylight.controller.sample.zeromq.consumer,
- org.opendaylight.controller.sal.utils,
- org.opendaylight.yangtools.yang.common,
- org.opendaylight.controller.sal.connector.api,
- org.opendaylight.controller.sal.connector.remoterpc.api;version="[0.4,1)",
- org.opendaylight.controller.sal.connector.remoterpc.impl;version="[0.4,1)",
- org.opendaylight.controller.sal.connector.remoterpc.dto,
- org.opendaylight.controller.sal.connector.remoterpc.util,
- org.osgi.framework,
- com.google.common.base,
- org.opendaylight.yangtools.yang.data.api,
- !org.codehaus.enunciate.jaxrs
-
- </Import-Package>
- <Web-ContextPath>/controller/nb/v2/zmqnb</Web-ContextPath>
- <Jaxrs-Resources>,${classes;ANNOTATION;javax.ws.rs.Path}</Jaxrs-Resources>
- </instructions>
- <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>containermanager</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>commons.northbound</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-provider</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller.tests</groupId>
- <artifactId>sal-remoterpc-connector-test-consumer</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-remoterpc-connector</artifactId>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>remoterpc-routingtable.implementation</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- </dependencies>
-
- </project>
+++ /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.tests.zmqrouter.rest;
-
-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.impl.RoutingTableImpl;
-import org.opendaylight.controller.sal.connector.remoterpc.util.XmlUtils;
-import org.opendaylight.controller.sample.zeromq.consumer.ExampleConsumer;
-import org.opendaylight.controller.sample.zeromq.provider.ExampleProvider;
-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.framework.ServiceReference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import java.io.Serializable;
-import java.net.URI;
-import java.util.Set;
-
-@Path("router")
-public class Router {
- private Logger _logger = LoggerFactory.getLogger(Router.class);
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "heartbeat");
-
-
- @GET
- @Path("/hello")
- @Produces(MediaType.TEXT_PLAIN)
- public String hello() {
- return "Hello";
- }
-
- @GET
- @Path("/announce")
- @Produces(MediaType.TEXT_PLAIN)
- public String announce() {
- _logger.info("Announce request received");
-
- BundleContext ctx = getBundleContext();
- ServiceReference providerRef = ctx.getServiceReference(ExampleProvider.class);
- if (providerRef == null) {
- _logger.debug("Could not get provider reference");
- return "Could not get provider reference";
- }
-
- ExampleProvider provider = (ExampleProvider) ctx.getService(providerRef);
- if (provider == null) {
- _logger.info("Could not get provider service");
- return "Could not get provider service";
- }
-
- provider.announce(QNAME);
- return "Announcement sent ";
-
- }
-
- @GET
- @Path("/rpc")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeRpc() throws Exception {
- _logger.info("Invoking RPC");
-
- ExampleConsumer consumer = getConsumer();
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, consumer.getValidCompositeNodeWithOneSimpleChild());
- _logger.info("Result [{}]", result.isSuccessful());
-
- return stringify(result);
- }
-
- @GET
- @Path("/rpc-success")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeRpcSuccess() throws Exception {
- ExampleConsumer consumer = getConsumer();
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, consumer.getValidCompositeNodeWithFourSimpleChildren()); //TODO: Change this
- _logger.info("Result [{}]", result.isSuccessful());
-
- return stringify(result);
- }
-
- @GET
- @Path("/rpc-failure")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeRpcFailure() throws Exception {
- ExampleConsumer consumer = getConsumer();
- //RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, consumer.getInvalidCompositeNodeCompositeChild()); //TODO: Change this
- RpcResult<CompositeNode> result = consumer.invokeRpc(QNAME, null); //TODO: Change this
- _logger.info("Result [{}]", result.isSuccessful());
-
- return stringify(result);
- }
-
- @GET
- @Path("/routingtable")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeRoutingTable() {
- _logger.info("Invoking adding an entry in routing table");
-
- BundleContext ctx = getBundleContext();
- ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class);
- if (routingTableServiceReference == null) {
- _logger.debug("Could not get routing table impl reference");
- return "Could not get routingtable referen ";
- }
- RoutingTableImpl routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference);
- if (routingTable == null) {
- _logger.info("Could not get routing table service");
- return "Could not get routing table service";
- }
-
-
- RoutingIdentifierImpl rii = new RoutingIdentifierImpl();
- try {
- routingTable.addGlobalRoute(rii.toString(), "172.27.12.1:5000");
- } catch (RoutingTableException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
-
- } catch (SystemException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
- }
-
- String result = routingTable.dumpRoutingTableCache();
-
-
-
-
- _logger.info("Result [{}] routes added for route" + rii + result);
-
- return result;
- }
-
- @GET
- @Path("/routingtabledelete")
- @Produces(MediaType.TEXT_PLAIN)
- public String invokeDeleteRoutingTable() {
- _logger.info("Invoking adding an entry in routing table");
-
- BundleContext ctx = getBundleContext();
- ServiceReference routingTableServiceReference = ctx.getServiceReference(RoutingTable.class);
- if (routingTableServiceReference == null) {
- _logger.debug("Could not get routing table impl reference");
- return "Could not get routingtable referen ";
- }
- RoutingTable routingTable = (RoutingTableImpl) ctx.getService(routingTableServiceReference);
- if (routingTable == null) {
- _logger.info("Could not get routing table service");
- return "Could not get routing table service";
- }
-
-
- RoutingIdentifierImpl rii = new RoutingIdentifierImpl();
- try {
- routingTable.removeGlobalRoute(rii.toString());
- } catch (RoutingTableException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
-
- } catch (SystemException e) {
- _logger.error("error in adding routing identifier" + e.getMessage());
- }
-
- Set<String> routes = routingTable.getRoutes(rii.toString());
-
- StringBuilder stringBuilder = new StringBuilder();
- if (routes != null) {
- for (String route : routes) {
- stringBuilder.append(route);
- }
- } else {
- stringBuilder.append(" successfully");
- }
-
- _logger.info("Result [{}] routes removed for route" + rii + stringBuilder.toString());
-
- return stringBuilder.toString();
- }
-
- private String stringify(RpcResult<CompositeNode> result) {
- CompositeNode node = result.getResult();
- StringBuilder builder = new StringBuilder("result:").append(XmlUtils.compositeNodeToXml(node)).append("\n")
- .append("error:").append(result.getErrors()).append("\n");
-
- return builder.toString();
- }
-
- private BundleContext getBundleContext() {
- ClassLoader tlcl = Thread.currentThread().getContextClassLoader();
- Bundle bundle = null;
-
- if (tlcl instanceof BundleReference) {
- bundle = ((BundleReference) tlcl).getBundle();
- } else {
- _logger.info("Unable to determine the bundle context based on " +
- "thread context classloader.");
- bundle = FrameworkUtil.getBundle(this.getClass());
- }
- return (bundle == null ? null : bundle.getBundleContext());
- }
-
- private ExampleConsumer getConsumer() {
- BundleContext ctx = getBundleContext();
- ServiceReference consumerRef = ctx.getServiceReference(ExampleConsumer.class);
- if (consumerRef == null) {
- _logger.debug("Could not get consumer reference");
- throw new NullPointerException("Could not get consumer reference");
- }
- ExampleConsumer consumer = (ExampleConsumer) ctx.getService(consumerRef);
- if (consumer == null) {
- _logger.info("Could not get consumer service");
- throw new NullPointerException("Could not get consumer service");
- }
- return consumer;
- }
-
- class RoutingIdentifierImpl implements RpcRouter.RouteIdentifier, Serializable {
-
- private final URI namespace = URI.create("http://cisco.com/example");
- private final QName QNAME = new QName(namespace, "global");
- private final QName instance = new QName(URI.create("127.0.0.1"), "local");
-
- @Override
- public QName getContext() {
- return QNAME;
- }
-
- @Override
- public QName getType() {
- return QNAME;
- }
-
- @Override
- public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier getRoute() {
- return InstanceIdentifier.of(instance);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- RoutingIdentifierImpl that = (RoutingIdentifierImpl) o;
-
- if (!QNAME.equals(that.QNAME)) return false;
- if (!instance.equals(that.instance)) return false;
- if (!namespace.equals(that.namespace)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = namespace.hashCode();
- result = 31 * result + QNAME.hashCode();
- result = 31 * result + instance.hashCode();
- return result;
- }
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
- version="3.0">
- <servlet>
- <servlet-name>JAXRSZmq</servlet-name>
- <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
- <init-param>
- <param-name>javax.ws.rs.Application</param-name>
- <param-value>org.opendaylight.controller.northbound.commons.NorthboundApplication</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>JAXRSZmq</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
-
-
-
- <security-constraint>
- <web-resource-collection>
- <web-resource-name>NB api</web-resource-name>
- <url-pattern>/*</url-pattern>
- <http-method>POST</http-method>
- <http-method>GET</http-method>
- <http-method>PUT</http-method>
- <http-method>PATCH</http-method>
- <http-method>DELETE</http-method>
- <http-method>HEAD</http-method>
- </web-resource-collection>
- <auth-constraint>
- <role-name>System-Admin</role-name>
- <role-name>Network-Admin</role-name>
- <role-name>Network-Operator</role-name>
- <role-name>Container-User</role-name>
- </auth-constraint>
- </security-constraint>
-
- <security-role>
- <role-name>System-Admin</role-name>
- </security-role>
- <security-role>
- <role-name>Network-Admin</role-name>
- </security-role>
- <security-role>
- <role-name>Network-Operator</role-name>
- </security-role>
- <security-role>
- <role-name>Container-User</role-name>
- </security-role>
-
- <login-config>
- <auth-method>BASIC</auth-method>
- <realm-name>opendaylight</realm-name>
- </login-config>
-</web-app>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-remote</artifactId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<build>
import com.google.common.base.Preconditions;
import com.google.gson.stream.JsonWriter;
+
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import javax.activation.UnsupportedDataTypeException;
+
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
import org.slf4j.LoggerFactory;
class JsonMapper {
+ private static final Logger LOG = LoggerFactory.getLogger(JsonMapper.class);
+ private final MountInstance mountPoint;
- private MountInstance mountPoint;
- private final Logger logger = LoggerFactory.getLogger(JsonMapper.class);
+ public JsonMapper(final MountInstance mountPoint) {
+ this.mountPoint = mountPoint;
+ }
- public void write(final JsonWriter writer, final CompositeNode data, final DataNodeContainer schema, final MountInstance mountPoint)
+ public void write(final JsonWriter writer, final CompositeNode data, final DataNodeContainer schema)
throws IOException {
Preconditions.checkNotNull(writer);
Preconditions.checkNotNull(data);
Preconditions.checkNotNull(schema);
- this.mountPoint = mountPoint;
writer.beginObject();
final Set<QName> foundLists = new HashSet<>();
Set<DataSchemaNode> parentSchemaChildNodes = parentSchema == null ?
- Collections.<DataSchemaNode>emptySet() : parentSchema.getChildNodes();
+ Collections.<DataSchemaNode>emptySet() : parentSchema.getChildNodes();
- for (Node<?> child : parent.getValue()) {
- DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchemaChildNodes);
+ for (Node<?> child : parent.getValue()) {
+ DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchemaChildNodes);
- if (childSchema == null) {
- // Node may not conform to schema or allows "anyxml" - we'll process it.
+ if (childSchema == null) {
+ // Node may not conform to schema or allows "anyxml" - we'll process it.
- logger.debug( "No schema found for data node \"" + child.getNodeType() );
+ LOG.debug("No schema found for data node \"{}\"", child.getNodeType());
- if( !foundLists.contains( child.getNodeType() ) ) {
- handleNoSchemaFound( writer, child, parent );
+ if( !foundLists.contains( child.getNodeType() ) ) {
+ handleNoSchemaFound( writer, child, parent );
- // Since we don't have a schema, we don't know which nodes are supposed to be
- // lists so treat every one as a potential list to avoid outputting duplicates.
+ // Since we don't have a schema, we don't know which nodes are supposed to be
+ // lists so treat every one as a potential list to avoid outputting duplicates.
- foundLists.add( child.getNodeType() );
- }
- }
- else if (childSchema instanceof ContainerSchemaNode) {
- Preconditions.checkState(child instanceof CompositeNode,
- "Data representation of Container should be CompositeNode - " + child.getNodeType());
- writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema);
- } else if (childSchema instanceof ListSchemaNode) {
- if (!foundLists.contains( child.getNodeType() ) ) {
- Preconditions.checkState(child instanceof CompositeNode,
- "Data representation of List should be CompositeNode - " + child.getNodeType());
- foundLists.add( child.getNodeType() );
- writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema);
- }
- } else if (childSchema instanceof LeafListSchemaNode) {
- if (!foundLists.contains( child.getNodeType() ) ) {
- Preconditions.checkState(child instanceof SimpleNode<?>,
- "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
- foundLists.add( child.getNodeType() );
- writeLeafList(writer, parent, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
- }
- } else if (childSchema instanceof LeafSchemaNode) {
- Preconditions.checkState(child instanceof SimpleNode<?>,
- "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
- writeLeaf(writer, (SimpleNode<?>) child, (LeafSchemaNode) childSchema);
- } else if (childSchema instanceof AnyXmlSchemaNode) {
- if( child instanceof CompositeNode ) {
- writeContainer(writer, (CompositeNode) child, null);
- }
- else {
- handleNoSchemaFound( writer, child, parent );
+ foundLists.add( child.getNodeType() );
+ }
+ }
+ else if (childSchema instanceof ContainerSchemaNode) {
+ Preconditions.checkState(child instanceof CompositeNode,
+ "Data representation of Container should be CompositeNode - %s", child.getNodeType());
+ writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema);
+ } else if (childSchema instanceof ListSchemaNode) {
+ if (!foundLists.contains( child.getNodeType() ) ) {
+ Preconditions.checkState(child instanceof CompositeNode,
+ "Data representation of List should be CompositeNode - %s", child.getNodeType());
+ foundLists.add( child.getNodeType() );
+ writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema);
+ }
+ } else if (childSchema instanceof LeafListSchemaNode) {
+ if (!foundLists.contains( child.getNodeType() ) ) {
+ Preconditions.checkState(child instanceof SimpleNode<?>,
+ "Data representation of LeafList should be SimpleNode - %s", child.getNodeType());
+ foundLists.add( child.getNodeType() );
+ writeLeafList(writer, parent, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
+ }
+ } else if (childSchema instanceof LeafSchemaNode) {
+ Preconditions.checkState(child instanceof SimpleNode<?>,
+ "Data representation of LeafList should be SimpleNode - %s", child.getNodeType());
+ writeLeaf(writer, (SimpleNode<?>) child, (LeafSchemaNode) childSchema);
+ } else if (childSchema instanceof AnyXmlSchemaNode) {
+ if( child instanceof CompositeNode ) {
+ writeContainer(writer, (CompositeNode) child, null);
+ }
+ else {
+ handleNoSchemaFound( writer, child, parent );
+ }
+ } else {
+ throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, "
+ + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
+ }
}
- } else {
- throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, "
- + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
- }
- }
}
- private void writeValue( final JsonWriter writer, Object value ) throws IOException {
- if( value != null ) {
- writer.value( String.valueOf( value ) );
- }
- else {
- writer.value( "" );
- }
+ private static void writeValue(final JsonWriter writer, final Object value) throws IOException {
+ writer.value(value == null ? "" : String.valueOf(value));
}
private void handleNoSchemaFound( final JsonWriter writer, final Node<?> node,
- final CompositeNode parent ) throws IOException {
+ final CompositeNode parent ) throws IOException {
if( node instanceof SimpleNode<?> ) {
List<SimpleNode<?>> nodeLeafList = parent.getSimpleNodesByName( node.getNodeType() );
if( nodeLeafList.size() == 1 ) {
}
} else { // CompositeNode
Preconditions.checkState( node instanceof CompositeNode,
- "Data representation of Container should be CompositeNode - " + node.getNodeType() );
+ "Data representation of Container should be CompositeNode - %s", node.getNodeType());
List<CompositeNode> nodeList = parent.getCompositesByName( node.getNodeType() );
if( nodeList.size() == 1 ) {
}
}
- private DataSchemaNode findFirstSchemaForNode(final Node<?> node, final Set<DataSchemaNode> dataSchemaNode) {
+ private static DataSchemaNode findFirstSchemaForNode(final Node<?> node, final Set<DataSchemaNode> dataSchemaNode) {
for (DataSchemaNode dsn : dataSchemaNode) {
if (node.getNodeType().equals(dsn.getQName())) {
return dsn;
- } else if (dsn instanceof ChoiceNode) {
+ }
+ if (dsn instanceof ChoiceNode) {
for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
if (foundDsn != null) {
TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(type);
if (node.getValue() == null && !(baseType instanceof EmptyTypeDefinition)) {
- logger.debug("While generationg JSON output null value was found for type "
- + baseType.getClass().getSimpleName() + ".");
+ LOG.debug("While generationg JSON output null value was found for type {}.",
+ baseType.getClass().getSimpleName());
}
if (baseType instanceof IdentityrefTypeDefinition) {
}
}
- private void writeIdentityValuesDTOToJson(final JsonWriter writer, final IdentityValuesDTO valueDTO) throws IOException {
+ private static void writeIdentityValuesDTOToJson(final JsonWriter writer, final IdentityValuesDTO valueDTO) throws IOException {
StringBuilder result = new StringBuilder();
for (IdentityValue identityValue : valueDTO.getValuesWithNamespaces()) {
- result.append("/");
+ result.append('/');
writeModuleNameAndIdentifier(result, identityValue);
if (identityValue.getPredicates() != null && !identityValue.getPredicates().isEmpty()) {
for (Predicate predicate : identityValue.getPredicates()) {
IdentityValue identityValuePredicate = predicate.getName();
- result.append("[");
+ result.append('[');
if (identityValuePredicate == null) {
- result.append(".");
+ result.append('.');
} else {
writeModuleNameAndIdentifier(result, identityValuePredicate);
}
result.append("='");
result.append(predicate.getValue());
- result.append("'");
- result.append("]");
+ result.append("']");
}
}
}
writer.value(result.toString());
}
- private void writeModuleNameAndIdentifier(final StringBuilder result, final IdentityValue identityValue) {
+ private static void writeModuleNameAndIdentifier(final StringBuilder result, final IdentityValue identityValue) {
String moduleName = ControllerContext.getInstance().findModuleNameByNamespace(
URI.create(identityValue.getNamespace()));
if (moduleName != null && !moduleName.isEmpty()) {
result.append(moduleName);
- result.append(":");
+ result.append(':');
}
result.append(identityValue.getValue());
}
- private void writeStringRepresentation(final JsonWriter writer, final SimpleNode<?> node, final TypeDefinition<?> baseType,
+ private static void writeStringRepresentation(final JsonWriter writer, final SimpleNode<?> node, final TypeDefinition<?> baseType,
final Class<?> requiredType) throws IOException {
Object value = node.getValue();
- logger.debug("Value of " + baseType.getQName().getNamespace() + ":" + baseType.getQName().getLocalName()
- + " is not instance of " + requiredType.getClass() + " but is " + node.getValue().getClass());
+ LOG.debug("Value of {}:{} is not instance of {} but is {}",
+ baseType.getQName().getNamespace(), baseType.getQName().getLocalName(),
+ requiredType.getClass(), node.getValue().getClass());
if (value == null) {
writer.value("");
} else {
if (moduleName != null) {
nameForOutput = moduleName.toString();
} else {
- logger.info("Module '{}' was not found in schema from mount point", schema.getQName());
+ LOG.info("Module '{}' was not found in schema from mount point", schema.getQName());
}
}
writer.name(nameForOutput);
*/
package org.opendaylight.controller.sal.rest.impl;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterators;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
+import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import com.google.common.collect.Lists;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
+final class JsonReader {
+ private static final Logger LOG = LoggerFactory.getLogger(JsonReader.class);
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+
+ private JsonReader() {
-class JsonReader {
+ }
- public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedFormatException {
+ public static CompositeNodeWrapper read(final InputStream entityStream) throws UnsupportedFormatException {
JsonParser parser = new JsonParser();
JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
- if( rootElement.isJsonNull() )
- {
+ if (rootElement.isJsonNull()) {
//no content, so return null to indicate no input
return null;
}
Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
if (entrySetsOfRootJsonObject.size() != 1) {
throw new UnsupportedFormatException("Json Object should contain one element");
- } else {
- Entry<String, JsonElement> childEntry = Lists.newArrayList(entrySetsOfRootJsonObject).get(0);
- String firstElementName = childEntry.getKey();
- JsonElement firstElementType = childEntry.getValue();
- if (firstElementType.isJsonObject()) { // container in yang
- return createStructureWithRoot(firstElementName, firstElementType.getAsJsonObject());
- }
- if (firstElementType.isJsonArray()) { // list in yang
- if (firstElementType.getAsJsonArray().size() == 1) {
- JsonElement firstElementInArray = firstElementType.getAsJsonArray().get(0);
- if (firstElementInArray.isJsonObject()) {
- return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
- }
- throw new UnsupportedFormatException(
- "Array as the first element in Json Object can have only Object element");
+ }
+
+ Entry<String, JsonElement> childEntry = entrySetsOfRootJsonObject.iterator().next();
+ String firstElementName = childEntry.getKey();
+ JsonElement firstElementType = childEntry.getValue();
+ if (firstElementType.isJsonObject()) {
+ // container in yang
+ return createStructureWithRoot(firstElementName, firstElementType.getAsJsonObject());
+ }
+ if (firstElementType.isJsonArray()) {
+ // list in yang
+ if (firstElementType.getAsJsonArray().size() == 1) {
+ JsonElement firstElementInArray = firstElementType.getAsJsonArray().get(0);
+ if (firstElementInArray.isJsonObject()) {
+ return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
}
+ throw new UnsupportedFormatException(
+ "Array as the first element in Json Object can have only Object element");
}
- throw new UnsupportedFormatException(
- "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
}
+ throw new UnsupportedFormatException(
+ "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
}
- private CompositeNodeWrapper createStructureWithRoot(String rootObjectName, JsonObject rootObject) {
+ private static CompositeNodeWrapper createStructureWithRoot(final String rootObjectName, final JsonObject rootObject) {
CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFor(rootObjectName),
getLocalNameFor(rootObjectName));
for (Entry<String, JsonElement> childOfFirstNode : rootObject.entrySet()) {
return firstNode;
}
- private void addChildToParent(String childName, JsonElement childType, CompositeNodeWrapper parent) {
+ private static void addChildToParent(final String childName, final JsonElement childType, final CompositeNodeWrapper parent) {
if (childType.isJsonObject()) {
CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName));
parent.addValue(child);
} else if (childType.isJsonArray()) {
if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) {
parent.addValue(new EmptyNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName)));
-
} else {
for (JsonElement childOfChildType : childType.getAsJsonArray()) {
addChildToParent(childName, childOfChildType, parent);
String value = childPrimitive.getAsString().trim();
parent.addValue(new SimpleNodeWrapper(getNamespaceFor(childName), getLocalNameFor(childName),
resolveValueOfElement(value)));
+ } else {
+ LOG.debug("Ignoring unhandled child type {}", childType);
}
}
- private URI getNamespaceFor(String jsonElementName) {
- String[] moduleNameAndLocalName = jsonElementName.split(":");
- // it is not "moduleName:localName"
- if (moduleNameAndLocalName.length != 2) {
- return null;
+ private static URI getNamespaceFor(final String jsonElementName) {
+ final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+ // The string needs to me in form "moduleName:localName"
+ if (it.hasNext()) {
+ final String maybeURI = it.next();
+ if (Iterators.size(it) == 1) {
+ return URI.create(maybeURI);
+ }
}
- return URI.create(moduleNameAndLocalName[0]);
+
+ return null;
}
- private String getLocalNameFor(String jsonElementName) {
- String[] moduleNameAndLocalName = jsonElementName.split(":");
- // it is not "moduleName:localName"
- if (moduleNameAndLocalName.length != 2) {
- return jsonElementName;
- }
- return moduleNameAndLocalName[1];
+ private static String getLocalNameFor(final String jsonElementName) {
+ final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+ // The string needs to me in form "moduleName:localName"
+ final String ret = Iterators.get(it, 1, null);
+ return ret != null && !it.hasNext() ? ret : jsonElementName;
}
- private Object resolveValueOfElement(String value) {
+ private static Object resolveValueOfElement(final String value) {
// it could be instance-identifier Built-In Type
- if (value.startsWith("/")) {
+ if (!value.isEmpty() && value.charAt(0) == '/') {
IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson());
if (resolvedValue != null) {
return resolvedValue;
}
}
+
// it could be identityref Built-In Type
URI namespace = getNamespaceFor(value);
if (namespace != null) {
@Provider
@Consumes({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON,
- MediaType.APPLICATION_JSON })
+ MediaType.APPLICATION_JSON })
public enum JsonToCompositeNodeProvider implements MessageBodyReader<CompositeNode> {
INSTANCE;
private final static Logger LOG = LoggerFactory.getLogger( JsonToCompositeNodeProvider.class );
@Override
- public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
return true;
}
@Override
- public CompositeNode readFrom(Class<CompositeNode> type, Type genericType, Annotation[] annotations,
- MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
- throws IOException, WebApplicationException {
- JsonReader jsonReader = new JsonReader();
+ public CompositeNode readFrom(final Class<CompositeNode> type, final Type genericType, final Annotation[] annotations,
+ final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream)
+ throws IOException, WebApplicationException {
try {
- return jsonReader.read(entityStream);
+ return JsonReader.read(entityStream);
} catch (Exception e) {
- LOG.debug( "Error parsing json input", e );
+ LOG.debug( "Error parsing json input", e);
throw new RestconfDocumentedException(
- "Error parsing input: " + e.getMessage(),
- ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE );
+ "Error parsing input: " + e.getMessage(),
+ ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
}
}
public final class RestUtil {
+ // FIXME: BUG-1275: this is code duplicates data.impl.codec
+
public static final String SQUOTE = "'";
public static final String DQUOTE = "\"";
private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
- public final static TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
+ public final static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
TypeDefinition<?> superType = type;
while (superType.getBaseType() != null) {
superType = superType.getBaseType();
return superType;
}
- public static IdentityValuesDTO asInstanceIdentifier(String value, PrefixesMaping prefixMap) {
+ public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) {
String valueTrimmed = value.trim();
if (!valueTrimmed.startsWith("/")) {
return null;
return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO;
}
- private static String getIdAndPrefixAsStr(String pathPart) {
+ private static String getIdAndPrefixAsStr(final String pathPart) {
int predicateStartIndex = pathPart.indexOf("[");
return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
}
- private static IdentityValue toIdentity(String xPathPart, PrefixesMaping prefixMap) {
+ private static IdentityValue toIdentity(final String xPathPart, final PrefixesMaping prefixMap) {
String xPathPartTrimmed = xPathPart.trim();
if (xPathPartTrimmed.isEmpty()) {
return null;
return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix);
}
- private static List<Predicate> toPredicates(String predicatesStr, PrefixesMaping prefixMap) {
+ private static List<Predicate> toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) {
List<Predicate> result = new ArrayList<>();
List<String> predicates = new ArrayList<>();
Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
return result;
}
- private static String toPredicateValue(String predicatedValue) {
+ private static String toPredicateValue(final String predicatedValue) {
String predicatedValueTrimmed = predicatedValue.trim();
if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
&& (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
public static class PrefixMapingFromXml implements PrefixesMaping {
StartElement startElement = null;
- public PrefixMapingFromXml(StartElement startElement) {
+ public PrefixMapingFromXml(final StartElement startElement) {
this.startElement = startElement;
}
@Override
- public String getNamespace(String prefix) {
+ public String getNamespace(final String prefix) {
return startElement.getNamespaceContext().getNamespaceURI(prefix);
}
}
public static class PrefixMapingFromJson implements PrefixesMaping {
@Override
- public String getNamespace(String prefix) {
+ public String getNamespace(final String prefix) {
return prefix;
}
}
import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.ERROR_TYPE_QNAME;
import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.NAMESPACE;
+import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.stream.JsonWriter;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.gson.stream.JsonWriter;
-
/**
* This class defines an ExceptionMapper that handles RestconfDocumentedExceptions thrown by
* resource implementations and translates appropriately to restconf error response as defined in
private Object toJsonResponseBody( final ImmutableCompositeNode errorsNode,
final DataNodeContainer errorsSchemaNode ) {
- JsonMapper jsonMapper = new JsonMapper();
+ JsonMapper jsonMapper = new JsonMapper(null);
Object responseBody = null;
try {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
- JsonWriter writer = new JsonWriter( new OutputStreamWriter( outStream, "UTF-8" ) );
+ JsonWriter writer = new JsonWriter(new OutputStreamWriter( outStream, Charsets.UTF_8));
writer.setIndent( " " );
- jsonMapper.write( writer, errorsNode, errorsSchemaNode, null );
+ jsonMapper.write( writer, errorsNode, errorsSchemaNode);
writer.flush();
responseBody = outStream.toString( "UTF-8" );
- }
- catch( IOException e ) {
+ } catch( IOException e ) {
LOG.error( "Error writing error response body", e );
}
try {
doc = factory.newDocumentBuilder().parse(
new InputSource( new StringReader( errorInfoWithRoot ) ) );
- }
- catch( Exception e ) {
+ } catch( Exception e ) {
// TODO: what if the content is text that happens to contain invalid markup? Could
// wrap in CDATA and try again.
- LOG.warn( "Error parsing restconf error-info, \"" + errorInfo + "\", as XML: " +
- e.toString() );
+ LOG.warn( "Error parsing restconf error-info, \"{}\", as XML", errorInfo, e);
return null;
}
*/
package org.opendaylight.controller.sal.rest.impl;
+import com.google.common.base.Charsets;
+import com.google.gson.stream.JsonWriter;
+
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import com.google.gson.stream.JsonWriter;
-
@Provider
@Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON,
- Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON })
+ Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON })
public enum StructuredDataToJsonProvider implements MessageBodyWriter<StructuredData> {
INSTANCE;
@Override
- public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
return type.equals( StructuredData.class );
}
@Override
- public long getSize(StructuredData t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ public long getSize(final StructuredData t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
return -1;
}
@Override
- public void writeTo(StructuredData t, Class<?> type, Type genericType, Annotation[] annotations,
- MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
- throws IOException, WebApplicationException {
+ public void writeTo(final StructuredData t, final Class<?> type, final Type genericType, final Annotation[] annotations,
+ final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
+ throws IOException, WebApplicationException {
CompositeNode data = t.getData();
if (data == null) {
throw new RestconfDocumentedException(Response.Status.NOT_FOUND);
}
- JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, "UTF-8"));
+ JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8));
writer.setIndent(" ");
- JsonMapper jsonMapper = new JsonMapper();
- jsonMapper.write(writer, data, (DataNodeContainer) t.getSchema(), t.getMountPoint());
+ JsonMapper jsonMapper = new JsonMapper(t.getMountPoint());
+ jsonMapper.write(writer, data, (DataNodeContainer) t.getSchema());
writer.flush();
}
import javax.ws.rs.ext.Provider;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
@Provider
@Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML,
- Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+ Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public enum StructuredDataToXmlProvider implements MessageBodyWriter<StructuredData> {
INSTANCE;
- private final static Logger logger = LoggerFactory.getLogger(StructuredDataToXmlProvider.class);
+ private static final Logger LOG = LoggerFactory.getLogger(StructuredDataToXmlProvider.class);
+ private static final TransformerFactory FACTORY = TransformerFactory.newInstance();
+ private static final ThreadLocal<Transformer> TRANSFORMER = new ThreadLocal<Transformer>() {
+ @Override
+ protected Transformer initialValue() {
+ final Transformer ret;
+ try {
+ ret = FACTORY.newTransformer();
+ } catch (TransformerConfigurationException e) {
+ LOG.error("Failed to instantiate XML transformer", e);
+ throw new IllegalStateException("XML encoding currently unavailable", e);
+ }
+
+ ret.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+ ret.setOutputProperty(OutputKeys.METHOD, "xml");
+ ret.setOutputProperty(OutputKeys.INDENT, "yes");
+ ret.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ ret.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ return ret;
+ }
+ };
@Override
- public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
return type.equals( StructuredData.class );
}
@Override
- public long getSize(StructuredData t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ public long getSize(final StructuredData t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
return -1;
}
@Override
- public void writeTo(StructuredData t, Class<?> type, Type genericType, Annotation[] annotations,
- MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
- throws IOException, WebApplicationException {
+ public void writeTo(final StructuredData t, final Class<?> type, final Type genericType, final Annotation[] annotations,
+ final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
+ throws IOException, WebApplicationException {
CompositeNode data = t.getData();
if (data == null) {
throw new RestconfDocumentedException(Response.Status.NOT_FOUND);
}
- XmlMapper xmlMapper = new XmlMapper();
- Document domTree = xmlMapper.write(data, (DataNodeContainer) t.getSchema());
+ final Transformer trans;
+ try {
+ trans = TRANSFORMER.get();
+ } catch (RuntimeException e) {
+ throw new RestconfDocumentedException(e.getMessage(), ErrorType.TRANSPORT,
+ ErrorTag.OPERATION_FAILED);
+ }
+
+ // FIXME: BUG-1281: eliminate the intermediate Document
+ final Document domTree = new XmlMapper().write(data, (DataNodeContainer) t.getSchema());
try {
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
- transformer.setOutputProperty(OutputKeys.METHOD, "xml");
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
- transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
- transformer.transform(new DOMSource(domTree), new StreamResult(entityStream));
+ trans.transform(new DOMSource(domTree), new StreamResult(entityStream));
} catch (TransformerException e) {
- logger.error("Error during translation of Document to OutputStream", e);
- throw new RestconfDocumentedException( e.getMessage(), ErrorType.TRANSPORT,
- ErrorTag.OPERATION_FAILED );
+ LOG.error("Error during translation of Document to OutputStream", e);
+ throw new RestconfDocumentedException(e.getMessage(), ErrorType.TRANSPORT,
+ ErrorTag.OPERATION_FAILED);
}
}
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.util.concurrent.Futures;
+import java.util.Collections;
import java.util.concurrent.Future;
-
import javax.ws.rs.core.Response.Status;
-
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
public Future<RpcResult<TransactionStatus>> commitConfigurationDataDelete( final InstanceIdentifier path ) {
this.checkPreconditions();
-
- final DataModificationTransaction transaction = dataService.beginTransaction();
- LOG.info( "Delete Configuration via Restconf: {}", path );
- transaction.removeConfigurationData( path );
- return transaction.commit();
+ return deleteDataAtTarget(path,dataService.beginTransaction());
}
public Future<RpcResult<TransactionStatus>> commitConfigurationDataDeleteBehindMountPoint(
final MountInstance mountPoint, final InstanceIdentifier path ) {
this.checkPreconditions();
+ return deleteDataAtTarget(path,mountPoint.beginTransaction());
+ }
- final DataModificationTransaction transaction = mountPoint.beginTransaction();
- LOG.info( "Delete Configuration via Restconf: {}", path );
- transaction.removeConfigurationData( path );
+ private Future<RpcResult<TransactionStatus>> deleteDataAtTarget(final InstanceIdentifier path,
+ final DataModificationTransaction transaction) {
+ LOG.info("Delete Configuration via Restconf: {}", path);
+ CompositeNode redDataAtPath = transaction.readConfigurationData(path);
+ if (redDataAtPath == null) {
+ return Futures.immediateFuture(Rpcs.<TransactionStatus> getRpcResult(true, TransactionStatus.COMMITED,
+ Collections.<RpcError> emptyList()));
+ }
+ transaction.removeConfigurationData(path);
return transaction.commit();
}
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Preconditions;
+
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import com.google.common.base.Preconditions;
-
public final class CompositeNodeWrapper implements NodeWrapper<CompositeNode>, CompositeNode {
private MutableCompositeNode compositeNode;
name = new QName(namespace, localName);
}
- List<Node<?>> nodeValues = new ArrayList<>();
+ List<Node<?>> nodeValues = new ArrayList<>(values.size());
for (NodeWrapper<?> nodeWrapper : values) {
nodeValues.add(nodeWrapper.unwrap());
}
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Iterables;
+
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
public class ControllerContext implements SchemaContextListener {
private final static Logger LOG = LoggerFactory.getLogger( ControllerContext.class );
private final static String URI_ENCODING_CHAR_SET = "ISO-8859-1";
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+
private final BiMap<URI, String> uriToModuleName = HashBiMap.<URI, String> create();
private final Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
final boolean toMountPointIdentifier ) {
this.checkPreconditions();
- Iterable<String> split = Splitter.on( "/" ).split( restconfInstance );
- final ArrayList<String> encodedPathArgs = Lists.<String> newArrayList( split );
- final List<String> pathArgs = this.urlPathArgsDecode( encodedPathArgs );
- this.omitFirstAndLastEmptyString( pathArgs );
+ final List<String> pathArgs = urlPathArgsDecode( SLASH_SPLITTER.split( restconfInstance ) );
+ omitFirstAndLastEmptyString( pathArgs );
if( pathArgs.isEmpty() ) {
return null;
}
return iiWithSchemaNode;
}
- private List<String> omitFirstAndLastEmptyString( final List<String> list ) {
+ private static List<String> omitFirstAndLastEmptyString( final List<String> list ) {
if( list.isEmpty() ) {
return list;
}
public DataNodeContainer getDataNodeContainerFor( final InstanceIdentifier path ) {
this.checkPreconditions();
- final List<PathArgument> elements = path.getPath();
+ final Iterable<PathArgument> elements = path.getPathArguments();
PathArgument head = elements.iterator().next();
final QName startQName = head.getNodeType();
final Module initialModule = globalSchema.findModuleByNamespaceAndRevision(
public String toFullRestconfIdentifier( final InstanceIdentifier path ) {
this.checkPreconditions();
- final List<PathArgument> elements = path.getPath();
+ final Iterable<PathArgument> elements = path.getPathArguments();
final StringBuilder builder = new StringBuilder();
PathArgument head = elements.iterator().next();
final QName startQName = head.getNodeType();
}
String head = strings.iterator().next();
- final String nodeName = this.toNodeName( head );
+ final String nodeName = toNodeName( head );
final String moduleName = ControllerContext.toModuleName( head );
DataSchemaNode targetNode = null;
public boolean isInstantiatedDataSchema( final DataSchemaNode node ) {
return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode ||
- node instanceof ContainerSchemaNode || node instanceof ListSchemaNode ||
- node instanceof AnyXmlSchemaNode;
+ node instanceof ContainerSchemaNode || node instanceof ListSchemaNode ||
+ node instanceof AnyXmlSchemaNode;
}
private void addKeyValue( final HashMap<QName, Object> map, final DataSchemaNode node,
private static String toModuleName( final String str ) {
Preconditions.<String> checkNotNull( str );
- if( str.contains( ":" ) ) {
- final String[] args = str.split( ":" );
- if( args.length == 2 ) {
- return args[0];
+ if( str.indexOf( ':' ) != -1 ) {
+ final Iterable<String> args = COLON_SPLITTER.split( str );
+ if( Iterables.size( args ) == 2 ) {
+ return args.iterator().next();
}
}
return null;
}
- private String toNodeName( final String str ) {
- if( str.contains( ":" ) ) {
- final String[] args = str.split( ":" );
- if( args.length == 2 ) {
- return args[1];
+ private static String toNodeName( final String str ) {
+ if( str.indexOf( ':' ) != -1 ) {
+ final Iterable<String> args = COLON_SPLITTER.split( str );
+ if( Iterables.size( args ) == 2 ) {
+ return Iterables.get( args, 1 );
}
}
return str;
private QName toQName( final String name ) {
final String module = toModuleName( name );
- final String node = this.toNodeName( name );
+ final String node = toNodeName( name );
Set<Module> modules = globalSchema.getModules();
final Comparator<Module> comparator = new Comparator<Module>() {
}
}
- public List<String> urlPathArgsDecode( final List<String> strings ) {
+ public static List<String> urlPathArgsDecode( final Iterable<String> strings ) {
try {
List<String> decodedPathArgs = new ArrayList<String>();
for( final String pathArg : strings ) {
try {
builder.append( this.toUriString( keyValues.get( key ) ) );
} catch( UnsupportedEncodingException e ) {
- LOG.error( "Error parsing URI: " + keyValues.get( key ), e );
+ LOG.error( "Error parsing URI: {}", keyValues.get( key ), e );
return null;
}
}
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Preconditions;
+
import java.net.URI;
import java.util.Collections;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import com.google.common.base.Preconditions;
-
public final class EmptyNodeWrapper implements NodeWrapper<Node<?>>, Node<Void> {
private Node<?> unwrapped;
return composite;
}
- public void setComposite(boolean composite) {
+ public void setComposite(final boolean composite) {
this.composite = composite;
}
- public EmptyNodeWrapper(URI namespace, String localName) {
+ public EmptyNodeWrapper(final URI namespace, final String localName) {
this.localName = Preconditions.checkNotNull(localName);
this.namespace = namespace;
}
@Override
- public void setQname(QName name) {
+ public void setQname(final QName name) {
Preconditions.checkState(unwrapped == null, "Cannot change the object, due to data inconsistencies.");
this.name = name;
}
}
@Override
- public void setNamespace(URI namespace) {
+ public void setNamespace(final URI namespace) {
Preconditions.checkState(unwrapped == null, "Cannot change the object, due to data inconsistencies.");
this.namespace = namespace;
}
}
@Override
+ @Deprecated
public CompositeNode getParent() {
return unwrap().getParent();
}
}
@Override
- public Void setValue(Void value) {
+ public Void setValue(final Void value) {
return null;
}
private RestCodec() {
}
- public static final Codec<Object, Object> from(TypeDefinition<?> typeDefinition, MountInstance mountPoint) {
+ public static final Codec<Object, Object> from(final TypeDefinition<?> typeDefinition, final MountInstance mountPoint) {
return new ObjectCodec(typeDefinition, mountPoint);
}
private final TypeDefinition<?> type;
- private ObjectCodec(TypeDefinition<?> typeDefinition, MountInstance mountPoint) {
+ private ObjectCodec(final TypeDefinition<?> typeDefinition, final MountInstance mountPoint) {
type = RestUtil.resolveBaseTypeFrom(typeDefinition);
if (type instanceof IdentityrefTypeDefinition) {
identityrefCodec = new IdentityrefCodecImpl(mountPoint);
@SuppressWarnings("unchecked")
@Override
- public Object deserialize(Object input) {
+ public Object deserialize(final Object input) {
try {
if (type instanceof IdentityrefTypeDefinition) {
if (input instanceof IdentityValuesDTO) {
}
}
} catch (ClassCastException e) { // TODO remove this catch when
- // everyone use codecs
+ // everyone use codecs
logger.error(
"ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
e);
@SuppressWarnings("unchecked")
@Override
- public Object serialize(Object input) {
+ public Object serialize(final Object input) {
try {
if (type instanceof IdentityrefTypeDefinition) {
return identityrefCodec.serialize(input);
}
}
} catch (ClassCastException e) { // TODO remove this catch when
- // everyone use codecs
+ // everyone use codecs
logger.error(
"ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
e);
private final MountInstance mountPoint;
- public IdentityrefCodecImpl(MountInstance mountPoint) {
+ public IdentityrefCodecImpl(final MountInstance mountPoint) {
this.mountPoint = mountPoint;
}
@Override
- public IdentityValuesDTO serialize(QName data) {
+ public IdentityValuesDTO serialize(final QName data) {
return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), data.getPrefix(),null);
}
@Override
- public QName deserialize(IdentityValuesDTO data) {
+ public QName deserialize(final IdentityValuesDTO data) {
IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint);
if (module == null) {
public static class LeafrefCodecImpl implements LeafrefCodec<String> {
@Override
- public String serialize(Object data) {
+ public String serialize(final Object data) {
return String.valueOf(data);
}
@Override
- public Object deserialize(String data) {
+ public Object deserialize(final String data) {
return data;
}
private final Logger logger = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class);
private final MountInstance mountPoint;
- public InstanceIdentifierCodecImpl(MountInstance mountPoint) {
+ public InstanceIdentifierCodecImpl(final MountInstance mountPoint) {
this.mountPoint = mountPoint;
}
@Override
- public IdentityValuesDTO serialize(InstanceIdentifier data) {
- List<PathArgument> pathArguments = data.getPath();
+ public IdentityValuesDTO serialize(final InstanceIdentifier data) {
IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO();
- for (PathArgument pathArgument : pathArguments) {
+ for (PathArgument pathArgument : data.getPathArguments()) {
IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType());
if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) {
List<Predicate> predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument)
}
@Override
- public InstanceIdentifier deserialize(IdentityValuesDTO data) {
+ public InstanceIdentifier deserialize(final IdentityValuesDTO data) {
List<PathArgument> result = new ArrayList<PathArgument>();
IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint);
}
}
- return result.isEmpty() ? null : new InstanceIdentifier(result);
+ return result.isEmpty() ? null : InstanceIdentifier.create(result);
}
- private List<Predicate> keyValuesToPredicateList(Map<QName, Object> keyValues) {
+ private List<Predicate> keyValuesToPredicateList(final Map<QName, Object> keyValues) {
List<Predicate> result = new ArrayList<>();
for (QName qName : keyValues.keySet()) {
Object value = keyValues.get(qName);
return result;
}
- private IdentityValue qNameToIdentityValue(QName qName) {
+ private IdentityValue qNameToIdentityValue(final QName qName) {
if (qName != null) {
return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName(), qName.getPrefix());
}
}
}
- private static Module getModuleByNamespace(String namespace, MountInstance mountPoint) {
+ private static Module getModuleByNamespace(final String namespace, final MountInstance mountPoint) {
URI validNamespace = resolveValidNamespace(namespace, mountPoint);
Module module = null;
return module;
}
- private static URI resolveValidNamespace(String namespace, MountInstance mountPoint) {
+ private static URI resolveValidNamespace(final String namespace, final MountInstance mountPoint) {
URI validNamespace;
if (mountPoint != null) {
validNamespace = ControllerContext.getInstance().findNamespaceByModuleName(mountPoint, namespace);
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
+
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
+
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
Set<Module> allModules = this.controllerContext.getAllModules();
for (final Module module : allModules) {
}
final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
QName qName = modulesSchemaNode.getQName();
final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
return new StructuredData(modulesNode, modulesSchemaNode, null);
final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
Module restconfModule = this.getRestconfModule();
final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
for (final String streamName : availableStreams) {
streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
}
final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
QName qName = streamsSchemaNode.getQName();
final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
return new StructuredData(streamsNode, streamsSchemaNode, null);
MountInstance mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
InstanceIdWithSchemaNode mountPointIdentifier =
- this.controllerContext.toMountPointIdentifier(identifier);
+ this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
modules = this.controllerContext.getAllModules(mountPoint);
}
else {
throw new RestconfDocumentedException(
"URI has bad format. If modules behind mount point should be showed, URI has to end with " +
- ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
Module restconfModule = this.getRestconfModule();
final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
for (final Module module : modules) {
modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
}
final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
QName qName = modulesSchemaNode.getQName();
final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
return new StructuredData(modulesNode, modulesSchemaNode, mountPoint);
MountInstance mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
InstanceIdWithSchemaNode mountPointIdentifier =
- this.controllerContext.toMountPointIdentifier(identifier);
+ this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
}
if (module == null) {
throw new RestconfDocumentedException(
"Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
- moduleNameAndRevision.getRevision() + "' was not found.",
- ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
+ moduleNameAndRevision.getRevision() + "' was not found.",
+ ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
}
Module restconfModule = this.getRestconfModule();
final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
}
MountInstance mountPoint = null;
if (identifier.contains(ControllerContext.MOUNT)) {
InstanceIdWithSchemaNode mountPointIdentifier =
- this.controllerContext.toMountPointIdentifier(identifier);
+ this.controllerContext.toMountPointIdentifier(identifier);
mountPoint = mountPointIdentifier.getMountPoint();
modules = this.controllerContext.getAllModules(mountPoint);
}
else {
throw new RestconfDocumentedException(
"URI has bad format. If operations behind mount point should be showed, URI has to end with " +
- ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
return this.operationsFromModulesToStructuredData(modules, mountPoint);
}
private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
- final MountInstance mountPoint) {
+ final MountInstance mountPoint) {
final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
Module restconfModule = this.getRestconfModule();
final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
- restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
+ restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
QName qName = operationsSchemaNode.getQName();
SchemaPath path = operationsSchemaNode.getPath();
ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
- new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
+ new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
for (final Module module : modules) {
Set<RpcDefinition> rpcs = module.getRpcs();
for (final RpcDefinition rpc : rpcs) {
QName rpcQName = rpc.getQName();
SimpleNode<Object> immutableSimpleNode =
- NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
+ NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
operationsAsData.add(immutableSimpleNode);
String name = module.getName();
}
final CompositeNode operationsNode =
- NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
+ NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
return new StructuredData(operationsNode, schemaNode, mountPoint);
}
final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
List<DataSchemaNode> instanceDataChildrenByName =
this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
- "name");
+ "name");
final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
- streamName));
+ streamName));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) streamSchemaNode), "description");
+ ((DataNodeContainer) streamSchemaNode), "description");
final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
- "DESCRIPTION_PLACEHOLDER"));
+ "DESCRIPTION_PLACEHOLDER"));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) streamSchemaNode), "replay-support");
+ ((DataNodeContainer) streamSchemaNode), "replay-support");
final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
- Boolean.valueOf(true)));
+ Boolean.valueOf(true)));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
+ ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
- null, ""));
+ null, ""));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) streamSchemaNode), "events");
+ ((DataNodeContainer) streamSchemaNode), "events");
final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
- null, ""));
+ null, ""));
return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
}
private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
List<DataSchemaNode> instanceDataChildrenByName =
- this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
- null, module.getName()));
+ null, module.getName()));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) moduleSchemaNode), "revision");
+ ((DataNodeContainer) moduleSchemaNode), "revision");
final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
Date _revision = module.getRevision();
moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
- REVISION_FORMAT.format(_revision)));
+ REVISION_FORMAT.format(_revision)));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) moduleSchemaNode), "namespace");
+ ((DataNodeContainer) moduleSchemaNode), "namespace");
final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
- module.getNamespace().toString()));
+ module.getNamespace().toString()));
instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
- ((DataNodeContainer) moduleSchemaNode), "feature");
+ ((DataNodeContainer) moduleSchemaNode), "feature");
final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
for (final FeatureDefinition feature : module.getFeatures()) {
moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
- feature.getQName().getLocalName()));
+ feature.getQName().getLocalName()));
}
return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
QName rpcName = rpc.getRpcDefinition().getQName();
URI rpcNamespace = rpcName.getNamespace();
if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
- Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
+ Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
}
return callRpc(rpc, payload);
}
- private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) {
+ private void validateInput(final DataSchemaNode inputSchema, final CompositeNode payload) {
if( inputSchema != null && payload == null )
{
//expected a non null payload
throw new RestconfDocumentedException( "Input is required.",
- ErrorType.PROTOCOL,
- ErrorTag.MALFORMED_MESSAGE );
+ ErrorType.PROTOCOL,
+ ErrorTag.MALFORMED_MESSAGE );
}
else if( inputSchema == null && payload != null )
{
//did not expect any input
throw new RestconfDocumentedException( "No input expected.",
- ErrorType.PROTOCOL,
- ErrorTag.MALFORMED_MESSAGE );
+ ErrorType.PROTOCOL,
+ ErrorTag.MALFORMED_MESSAGE );
}
//else
//{
- //TODO: Validate "mandatory" and "config" values here??? Or should those be
+ //TODO: Validate "mandatory" and "config" values here??? Or should those be
// validate in a more central location inside MD-SAL core.
//}
}
private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
- final RpcDefinition rpc) {
+ final RpcDefinition rpc) {
final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
final SimpleNode<? extends Object> pathNode = value == null ? null :
- value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
+ value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
final Object pathValue = pathNode == null ? null : pathNode.getValue();
if (!(pathValue instanceof InstanceIdentifier)) {
final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
String streamName = null;
- if (!Iterables.isEmpty(pathIdentifier.getPath())) {
+ if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
}
}
final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
- QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
+ QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
final List<Node<?>> output = new ArrayList<Node<?>>();
output.add(streamNameNode);
final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
- rpc.getOutput().getQName(), null, output, null, null);
+ rpc.getOutput().getQName(), null, output, null, null);
if (!Notificator.existListenerFor(pathIdentifier)) {
Notificator.createListener(pathIdentifier, streamName);
return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null);
}
- private void checkRpcSuccessAndThrowException(RpcResult<CompositeNode> rpcResult) {
+ private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
if (rpcResult.isSuccessful() == false) {
Collection<RpcError> rpcErrors = rpcResult.getErrors();
if( rpcErrors == null || rpcErrors.isEmpty() ) {
throw new RestconfDocumentedException(
- "The operation was not successful and there were no RPC errors returned",
- ErrorType.RPC, ErrorTag.OPERATION_FAILED );
+ "The operation was not successful and there were no RPC errors returned",
+ ErrorType.RPC, ErrorTag.OPERATION_FAILED );
}
List<RestconfError> errorList = Lists.newArrayList();
}
@Override
- public StructuredData readConfigurationData(final String identifier, UriInfo info) {
+ public StructuredData readConfigurationData(final String identifier, final UriInfo info) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
CompositeNode data = null;
MountInstance mountPoint = iiWithData.getMountPoint();
}
@SuppressWarnings("unchecked")
- private <T extends Node<?>> T pruneDataAtDepth( T node, Integer depth ) {
+ private <T extends Node<?>> T pruneDataAtDepth( final T node, final Integer depth ) {
if( depth == null ) {
return node;
}
}
}
- private Integer parseDepthParameter( UriInfo info ) {
+ private Integer parseDepthParameter( final UriInfo info ) {
String param = info.getQueryParameters( false ).getFirst( "depth" );
if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
return null;
}
@Override
- public StructuredData readOperationalData(final String identifier, UriInfo info) {
+ public StructuredData readOperationalData(final String identifier, final UriInfo info) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
CompositeNode data = null;
MountInstance mountPoint = iiWithData.getMountPoint();
try {
if (mountPoint != null) {
status = broker.commitConfigurationDataPutBehindMountPoint(
- mountPoint, iiWithData.getInstanceIdentifier(), value).get();
+ mountPoint, iiWithData.getInstanceIdentifier(), value).get();
} else {
status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
}
throw new RestconfDocumentedException( "Error updating data", e );
}
- if( status.getResult() == TransactionStatus.COMMITED )
+ if( status.getResult() == TransactionStatus.COMMITED ) {
return Response.status(Status.OK).build();
+ }
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
URI payloadNS = this.namespace(payload);
if (payloadNS == null) {
throw new RestconfDocumentedException(
- "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
- ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
+ "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
+ ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
}
InstanceIdWithSchemaNode iiWithData = null;
CompositeNode value = null;
if (this.representsMountPointRootData(payload)) {
- // payload represents mount point data and URI represents path to the mount point
+ // payload represents mount point data and URI represents path to the mount point
if (this.endsWithMountPoint(identifier)) {
throw new RestconfDocumentedException(
}
else {
final InstanceIdWithSchemaNode incompleteInstIdWithData =
- this.controllerContext.toInstanceIdentifier(identifier);
+ this.controllerContext.toInstanceIdentifier(identifier);
final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
final Module module = this.findModule(mountPoint, payload);
String payloadName = this.getName(payload);
final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
- parentSchema, payloadName, module.getNamespace());
+ parentSchema, payloadName, module.getNamespace());
value = this.normalizeNode(payload, schemaNode, mountPoint);
iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
try {
if (mountPoint != null) {
Future<RpcResult<TransactionStatus>> future =
- broker.commitConfigurationDataPostBehindMountPoint(
- mountPoint, iiWithData.getInstanceIdentifier(), value);
+ broker.commitConfigurationDataPostBehindMountPoint(
+ mountPoint, iiWithData.getInstanceIdentifier(), value);
status = future == null ? null : future.get();
}
else {
Future<RpcResult<TransactionStatus>> future =
- broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
+ broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
status = future == null ? null : future.get();
}
}
return Response.status(Status.ACCEPTED).build();
}
- if( status.getResult() == TransactionStatus.COMMITED )
+ if( status.getResult() == TransactionStatus.COMMITED ) {
return Response.status(Status.NO_CONTENT).build();
+ }
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
URI payloadNS = this.namespace(payload);
if (payloadNS == null) {
throw new RestconfDocumentedException(
- "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
- ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
+ "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
+ ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
}
final Module module = this.findModule(null, payload);
String payloadName = this.getName(payload);
final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
- module, payloadName, module.getNamespace());
+ module, payloadName, module.getNamespace());
final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
RpcResult<TransactionStatus> status = null;
try {
if (mountPoint != null) {
Future<RpcResult<TransactionStatus>> future =
- broker.commitConfigurationDataPostBehindMountPoint(
- mountPoint, iiWithData.getInstanceIdentifier(), value);
+ broker.commitConfigurationDataPostBehindMountPoint(
+ mountPoint, iiWithData.getInstanceIdentifier(), value);
status = future == null ? null : future.get();
}
else {
Future<RpcResult<TransactionStatus>> future =
- broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
+ broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
status = future == null ? null : future.get();
}
}
return Response.status(Status.ACCEPTED).build();
}
- if( status.getResult() == TransactionStatus.COMMITED )
+ if( status.getResult() == TransactionStatus.COMMITED ) {
return Response.status(Status.NO_CONTENT).build();
+ }
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
try {
if (mountPoint != null) {
status = broker.commitConfigurationDataDeleteBehindMountPoint(
- mountPoint, iiWithData.getInstanceIdentifier()).get();
+ mountPoint, iiWithData.getInstanceIdentifier()).get();
}
else {
status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
throw new RestconfDocumentedException( "Error creating data", e );
}
- if( status.getResult() == TransactionStatus.COMMITED )
+ if( status.getResult() == TransactionStatus.COMMITED ) {
return Response.status(Status.OK).build();
+ }
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
}
private InstanceIdWithSchemaNode addLastIdentifierFromData(
- final InstanceIdWithSchemaNode identifierWithSchemaNode,
- final CompositeNode data, final DataSchemaNode schemaOfData) {
+ final InstanceIdWithSchemaNode identifierWithSchemaNode,
+ final CompositeNode data, final DataSchemaNode schemaOfData) {
InstanceIdentifier instanceIdentifier = null;
if (identifierWithSchemaNode != null) {
instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
}
private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
- final CompositeNode dataNode) {
+ final CompositeNode dataNode) {
final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
List<QName> _keyDefinition = listNode.getKeyDefinition();
for (final QName key : _keyDefinition) {
private boolean endsWithMountPoint(final String identifier) {
return identifier.endsWith(ControllerContext.MOUNT) ||
- identifier.endsWith(ControllerContext.MOUNT + "/");
+ identifier.endsWith(ControllerContext.MOUNT + "/");
}
private boolean representsMountPointRootData(final CompositeNode data) {
}
private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
- final MountInstance mountPoint) {
+ final MountInstance mountPoint) {
if (schema == null) {
QName nodeType = node == null ? null : node.getNodeType();
String localName = nodeType == null ? null : nodeType.getLocalName();
}
catch (IllegalArgumentException e) {
throw new RestconfDocumentedException(
- e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
}
}
private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
- final DataSchemaNode schema, final QName previousAugment,
- final MountInstance mountPoint) {
+ final DataSchemaNode schema, final QName previousAugment,
+ final MountInstance mountPoint) {
if (schema == null) {
throw new RestconfDocumentedException(
"Data has bad format.\n\"" + nodeBuilder.getLocalName() +
if (nodeBuilder.getQname() == null) {
throw new RestconfDocumentedException(
"Data has bad format.\nIf data is in XML format then namespace for \"" +
- nodeBuilder.getLocalName() +
- "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
- "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
- "\" should be corresponding to namespace \"" +
- schema.getQName().getNamespace() + "\".",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ nodeBuilder.getLocalName() +
+ "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
+ "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
+ "\" should be corresponding to namespace \"" +
+ schema.getQName().getNamespace() + "\".",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
}
if ( nodeBuilder instanceof CompositeNodeWrapper ) {
if( schema instanceof DataNodeContainer ) {
normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema,
- mountPoint, currentAugment );
+ mountPoint, currentAugment );
}
else if( schema instanceof AnyXmlSchemaNode ) {
normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema );
}
}
- private void normalizeAnyXmlNode( CompositeNodeWrapper compositeNode, AnyXmlSchemaNode schema ) {
+ private void normalizeAnyXmlNode( final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema ) {
List<NodeWrapper<?>> children = compositeNode.getValues();
for( NodeWrapper<? extends Object> child : children ) {
child.setNamespace( schema.getQName().getNamespace() );
}
}
- private void normalizeEmptyNode( EmptyNodeWrapper emptyNodeBuilder, DataSchemaNode schema ) {
+ private void normalizeEmptyNode( final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema ) {
if ((schema instanceof LeafSchemaNode)) {
emptyNodeBuilder.setComposite(false);
}
}
}
- private void normalizeSimpleNode( SimpleNodeWrapper simpleNode, DataSchemaNode schema,
- MountInstance mountPoint ) {
+ private void normalizeSimpleNode( final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
+ final MountInstance mountPoint ) {
final Object value = simpleNode.getValue();
Object inputValue = value;
TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
simpleNode.setValue(outputValue);
}
- private void normalizeCompositeNode( CompositeNodeWrapper compositeNodeBuilder,
- DataNodeContainer schema, MountInstance mountPoint,
- QName currentAugment ) {
+ private void normalizeCompositeNode( final CompositeNodeWrapper compositeNodeBuilder,
+ final DataNodeContainer schema, final MountInstance mountPoint,
+ final QName currentAugment ) {
final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
for (final NodeWrapper<? extends Object> child : children) {
final List<DataSchemaNode> potentialSchemaNodes =
this.controllerContext.findInstanceDataChildrenByName(
- schema, child.getLocalName());
+ schema, child.getLocalName());
if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
StringBuilder builder = new StringBuilder();
for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString())
- .append("\n");
+ .append("\n");
}
throw new RestconfDocumentedException(
- "Node \"" + child.getLocalName() +
- "\" is added as augment from more than one module. " +
- "Therefore node must have namespace (XML format) or module name (JSON format)." +
- "\nThe node is added as augment from modules with namespaces:\n" + builder,
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ "Node \"" + child.getLocalName() +
+ "\" is added as augment from more than one module. " +
+ "Therefore node must have namespace (XML format) or module name (JSON format)." +
+ "\nThe node is added as augment from modules with namespaces:\n" + builder,
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
boolean rightNodeSchemaFound = false;
if (!rightNodeSchemaFound) {
throw new RestconfDocumentedException(
- "Schema node \"" + child.getLocalName() + "\" was not found in module.",
- ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
+ "Schema node \"" + child.getLocalName() + "\" was not found in module.",
+ ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
}
}
if (!foundKey) {
throw new RestconfDocumentedException(
- "Missing key in URI \"" + listKey.getLocalName() +
- "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
- ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+ "Missing key in URI \"" + listKey.getLocalName() +
+ "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
}
}
}
private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
- final DataSchemaNode schema, final QName previousAugment,
- final MountInstance mountPoint) {
+ final DataSchemaNode schema, final QName previousAugment,
+ final MountInstance mountPoint) {
QName validQName = schema.getQName();
QName currentAugment = previousAugment;
if (schema.isAugmenting()) {
currentAugment = schema.getQName();
}
else if (previousAugment != null &&
- !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
+ !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
validQName = QName.create(currentAugment, schema.getQName().getLocalName());
}
}
if (nodeBuilder.getNamespace() == null ||
- Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
- Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
+ Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
+ Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
Note: this check is wrong - can never be true as it compares a URI with a String
not sure what the intention is so commented out...
Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Preconditions;
+
import java.net.URI;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import com.google.common.base.Preconditions;
-
public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, SimpleNode<Object> {
private SimpleNode<Object> simpleNode;
private URI namespace;
private QName name;
- public SimpleNodeWrapper(String localName, Object value) {
+ public SimpleNodeWrapper(final String localName, final Object value) {
this.localName = Preconditions.checkNotNull(localName);
this.value = value;
}
- public SimpleNodeWrapper(URI namespace, String localName, Object value) {
+ public SimpleNodeWrapper(final URI namespace, final String localName, final Object value) {
this(localName, value);
this.namespace = namespace;
}
@Override
- public void setQname(QName name) {
+ public void setQname(final QName name) {
Preconditions.checkState(simpleNode == null, "Cannot change the object, due to data inconsistencies.");
this.name = name;
}
}
@Override
- public void setNamespace(URI namespace) {
+ public void setNamespace(final URI namespace) {
Preconditions.checkState(simpleNode == null, "Cannot change the object, due to data inconsistencies.");
this.namespace = namespace;
}
localName = null;
name = null;
}
- return (SimpleNode<Object>) simpleNode;
+ return simpleNode;
}
@Override
}
@Override
+ @Deprecated
public CompositeNode getParent() {
return unwrap().getParent();
}
}
@Override
+ @Deprecated
public ModifyAction getModificationAction() {
return unwrap().getModificationAction();
}
}
@Override
- public Object setValue(Object value) {
+ public Object setValue(final Object value) {
return unwrap().setValue(value);
}
*/
package org.opendaylight.controller.sal.streams.listeners;
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+import com.google.common.eventbus.AsyncEventBus;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.internal.ConcurrentSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executors;
+import java.util.regex.Pattern;
import javax.activation.UnsupportedDataTypeException;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
-import com.google.common.base.Preconditions;
-import com.google.common.eventbus.AsyncEventBus;
-import com.google.common.eventbus.EventBus;
-import com.google.common.eventbus.Subscribe;
-
/**
* {@link ListenerAdapter} is responsible to track events, which occurred by
* changing data in data source.
*/
public class ListenerAdapter implements DataChangeListener {
- private static final Logger logger = LoggerFactory
- .getLogger(ListenerAdapter.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapter.class);
+ private static final DocumentBuilderFactory DBF = DocumentBuilderFactory.newInstance();
+ private static final TransformerFactory FACTORY = TransformerFactory.newInstance();
+ private static final Pattern RFC3339_PATTERN = Pattern.compile("(\\d\\d)(\\d\\d)$");
+
private final XmlMapper xmlMapper = new XmlMapper();
private final SimpleDateFormat rfc3339 = new SimpleDateFormat(
"yyyy-MM-dd'T'hh:mm:ssZ");
private final EventBus eventBus;
private final EventBusChangeRecorder eventBusChangeRecorder;
+
/**
* Creates new {@link ListenerAdapter} listener specified by path and stream
* name.
* @param streamName
* The name of the stream.
*/
- ListenerAdapter(InstanceIdentifier path, String streamName) {
+ ListenerAdapter(final InstanceIdentifier path, final String streamName) {
Preconditions.checkNotNull(path);
Preconditions
- .checkArgument(streamName != null && !streamName.isEmpty());
+ .checkArgument(streamName != null && !streamName.isEmpty());
this.path = path;
this.streamName = streamName;
eventBus = new AsyncEventBus(Executors.newSingleThreadExecutor());
@Override
public void onDataChanged(
- DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
+ final DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
if (!change.getCreatedConfigurationData().isEmpty()
|| !change.getCreatedOperationalData().isEmpty()
|| !change.getUpdatedConfigurationData().isEmpty()
*/
private final class EventBusChangeRecorder {
@Subscribe
- public void recordCustomerChange(Event event) {
+ public void recordCustomerChange(final Event event) {
if (event.getType() == EventType.REGISTER) {
Channel subscriber = event.getSubscriber();
if (!subscribers.contains(subscriber)) {
} else if (event.getType() == EventType.DEREGISTER) {
subscribers.remove(event.getSubscriber());
Notificator
- .removeListenerIfNoSubscriberExists(ListenerAdapter.this);
+ .removeListenerIfNoSubscriberExists(ListenerAdapter.this);
} else if (event.getType() == EventType.NOTIFY) {
for (Channel subscriber : subscribers) {
if (subscriber.isActive()) {
- logger.debug("Data are sent to subscriber {}:",
+ LOG.debug("Data are sent to subscriber {}:",
subscriber.remoteAddress());
subscriber.writeAndFlush(new TextWebSocketFrame(event
.getData()));
} else {
- logger.debug(
+ LOG.debug(
"Subscriber {} is removed - channel is not active yet.",
subscriber.remoteAddress());
subscribers.remove(subscriber);
* @param type
* EventType
*/
- public Event(EventType type) {
+ public Event(final EventType type) {
this.type = type;
}
* @param subscriber
* Channel
*/
- public void setSubscriber(Channel subscriber) {
+ public void setSubscriber(final Channel subscriber) {
this.subscriber = subscriber;
}
* @param String
* data.
*/
- public void setData(String data) {
+ public void setData(final String data) {
this.data = data;
}
* @return Data in printable form.
*/
private String prepareXmlFrom(
- DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
+ final DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
Document doc = createDocument();
Element notificationElement = doc.createElementNS(
"urn:ietf:params:xml:ns:netconf:notification:1.0",
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- TransformerFactory tf = TransformerFactory.newInstance();
- Transformer transformer = tf.newTransformer();
- transformer
- .setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+ Transformer transformer = FACTORY.newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- transformer.setOutputProperty(
- "{http://xml.apache.org/xslt}indent-amount", "4");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(doc), new StreamResult(
- new OutputStreamWriter(out, "UTF-8")));
+ new OutputStreamWriter(out, Charsets.UTF_8)));
byte[] charData = out.toByteArray();
return new String(charData, "UTF-8");
} catch (TransformerException | UnsupportedEncodingException e) {
String msg = "Error during transformation of Document into String";
- logger.error(msg, e);
+ LOG.error(msg, e);
return msg;
}
}
* Date
* @return Data specified by RFC3339.
*/
- private String toRFC3339(Date d) {
- return rfc3339.format(d).replaceAll("(\\d\\d)(\\d\\d)$", "$1:$2");
+ private String toRFC3339(final Date d) {
+ return RFC3339_PATTERN.matcher(rfc3339.format(d)).replaceAll("$1:$2");
}
/**
* @return {@link Document} document.
*/
private Document createDocument() {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- Document doc = null;
+ final DocumentBuilder bob;
try {
- DocumentBuilder bob = dbf.newDocumentBuilder();
- doc = bob.newDocument();
+ bob = DBF.newDocumentBuilder();
} catch (ParserConfigurationException e) {
return null;
}
- return doc;
+ return bob.newDocument();
}
/**
* @param change
* {@link DataChangeEvent}
*/
- private void addValuesToDataChangedNotificationEventElement(Document doc,
- Element dataChangedNotificationEventElement,
- DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
+ private void addValuesToDataChangedNotificationEventElement(final Document doc,
+ final Element dataChangedNotificationEventElement,
+ final DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
addValuesFromDataToElement(doc, change.getCreatedConfigurationData(),
dataChangedNotificationEventElement, Store.CONFIG,
Operation.CREATED);
* @param operation
* {@link Operation}
*/
- private void addValuesFromDataToElement(Document doc,
- Set<InstanceIdentifier> data, Element element, Store store,
- Operation operation) {
+ private void addValuesFromDataToElement(final Document doc,
+ final Set<InstanceIdentifier> data, final Element element, final Store store,
+ final Operation operation) {
if (data == null || data.isEmpty()) {
return;
}
* @param operation
* {@link Operation}
*/
- private void addValuesFromDataToElement(Document doc,
- Map<InstanceIdentifier, CompositeNode> data, Element element,
- Store store, Operation operation) {
+ private void addValuesFromDataToElement(final Document doc,
+ final Map<InstanceIdentifier, CompositeNode> data, final Element element,
+ final Store store, final Operation operation) {
if (data == null || data.isEmpty()) {
return;
}
* {@link Operation}
* @return {@link Node} node represented by changed event element.
*/
- private Node createDataChangeEventElement(Document doc,
- InstanceIdentifier path, CompositeNode data, Store store,
- Operation operation) {
+ private Node createDataChangeEventElement(final Document doc,
+ final InstanceIdentifier path, final CompositeNode data, final Store store,
+ final Operation operation) {
Element dataChangeEventElement = doc.createElement("data-change-event");
Element pathElement = doc.createElement("path");
* {@link CompositeNode}
* @return Data in XML format.
*/
- private Node translateToXml(InstanceIdentifier path, CompositeNode data) {
+ private Node translateToXml(final InstanceIdentifier path, final CompositeNode data) {
DataNodeContainer schemaNode = ControllerContext.getInstance()
.getDataNodeContainerFor(path);
if (schemaNode == null) {
- logger.info(
+ LOG.info(
"Path '{}' contains node with unsupported type (supported type is Container or List) or some node was not found.",
path);
return null;
Document xml = xmlMapper.write(data, schemaNode);
return xml.getFirstChild();
} catch (UnsupportedDataTypeException e) {
- logger.error(
+ LOG.error(
"Error occured during translation of notification to XML.",
e);
return null;
* @param element
* {@link Element}
*/
- private void addPathAsValueToElement(InstanceIdentifier path,
- Element element) {
+ private void addPathAsValueToElement(final InstanceIdentifier path,
+ final Element element) {
// Map< key = namespace, value = prefix>
Map<String, String> prefixes = new HashMap<>();
InstanceIdentifier instanceIdentifier = path;
StringBuilder textContent = new StringBuilder();
- for (PathArgument pathArgument : instanceIdentifier.getPath()) {
+
+ // FIXME: BUG-1281: this is duplicated code from yangtools (BUG-1275)
+ for (PathArgument pathArgument : instanceIdentifier.getPathArguments()) {
textContent.append("/");
writeIdentifierWithNamespacePrefix(element, textContent,
pathArgument.getNodeType(), prefixes);
* @param prefixes
* Map of namespaces and prefixes.
*/
- private static void writeIdentifierWithNamespacePrefix(Element element,
- StringBuilder textContent, QName qName, Map<String, String> prefixes) {
+ private static void writeIdentifierWithNamespacePrefix(final Element element,
+ final StringBuilder textContent, final QName qName, final Map<String, String> prefixes) {
String namespace = qName.getNamespace().toString();
String prefix = prefixes.get(namespace);
if (prefix == null) {
* Collection of prefixes.
* @return New prefix which consists of four random characters <a-z>.
*/
- private static String generateNewPrefix(Collection<String> prefixes) {
+ private static String generateNewPrefix(final Collection<String> prefixes) {
StringBuilder result = null;
Random random = new Random();
do {
* ListenerRegistration<DataChangeListener>
*/
public void setRegistration(
- ListenerRegistration<DataChangeListener> registration) {
+ final ListenerRegistration<DataChangeListener> registration) {
this.registration = registration;
}
* @param subscriber
* Channel
*/
- public void addSubscriber(Channel subscriber) {
+ public void addSubscriber(final Channel subscriber) {
if (!subscriber.isActive()) {
- logger.debug("Channel is not active between websocket server and subscriber {}"
+ LOG.debug("Channel is not active between websocket server and subscriber {}"
+ subscriber.remoteAddress());
}
Event event = new Event(EventType.REGISTER);
*
* @param subscriber
*/
- public void removeSubscriber(Channel subscriber) {
- logger.debug("Subscriber {} is removed.", subscriber.remoteAddress());
+ public void removeSubscriber(final Channel subscriber) {
+ LOG.debug("Subscriber {} is removed.", subscriber.remoteAddress());
Event event = new Event(EventType.DEREGISTER);
event.setSubscriber(subscriber);
eventBus.post(event);
private final String value;
- private Store(String value) {
+ private Store(final String value) {
this.value = value;
}
}
private final String value;
- private Operation(String value) {
+ private Operation(final String value) {
this.value = value;
}
}
private WebSocketServerHandshaker handshaker;
@Override
- protected void channelRead0(ChannelHandlerContext ctx, Object msg)
+ protected void channelRead0(final ChannelHandlerContext ctx, final Object msg)
throws Exception {
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
* @param req
* FullHttpRequest
*/
- private void handleHttpRequest(ChannelHandlerContext ctx,
- FullHttpRequest req) throws Exception {
+ private void handleHttpRequest(final ChannelHandlerContext ctx,
+ final FullHttpRequest req) throws Exception {
// Handle a bad request.
if (!req.getDecoderResult().isSuccess()) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,
getWebSocketLocation(req), null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
- WebSocketServerHandshakerFactory
- .sendUnsupportedWebSocketVersionResponse(ctx.channel());
+ WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), req);
}
* @param res
* FullHttpResponse
*/
- private static void sendHttpResponse(ChannelHandlerContext ctx,
- HttpRequest req, FullHttpResponse res) {
+ private static void sendHttpResponse(final ChannelHandlerContext ctx,
+ final HttpRequest req, final FullHttpResponse res) {
// Generate an error page if response getStatus code is not OK (200).
if (res.getStatus().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),
* @param frame
* {@link WebSocketFrame}
*/
- private void handleWebSocketFrame(ChannelHandlerContext ctx,
- WebSocketFrame frame) throws IOException {
+ private void handleWebSocketFrame(final ChannelHandlerContext ctx,
+ final WebSocketFrame frame) throws IOException {
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(),
(CloseWebSocketFrame) frame.retain());
}
@Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
+ public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause)
throws Exception {
if (cause instanceof java.nio.channels.ClosedChannelException == false) {
// cause.printStackTrace();
* HTTP request from which the location will be returned
* @return String representation of web socket location.
*/
- private static String getWebSocketLocation(HttpRequest req) {
+ private static String getWebSocketLocation(final HttpRequest req) {
return "http://" + req.headers().get(HOST) + req.getUri();
}
BitsTypeDefinition.Bit mockBit1 = mock( BitsTypeDefinition.Bit.class );
when( mockBit1.getName() ).thenReturn( "first" );
BitsTypeDefinition.Bit mockBit2 = mock( BitsTypeDefinition.Bit.class );
- when( mockBit1.getName() ).thenReturn( "second" );
+ when( mockBit2.getName() ).thenReturn( "second" );
List<BitsTypeDefinition.Bit> bitList = Lists.newArrayList( mockBit1, mockBit2 );
List<TypeDefinition<?>> types = Lists.<TypeDefinition<?>>newArrayList(
*/
package org.opendaylight.controller.sal.restconf.impl.test;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
import java.util.Map;
import java.util.concurrent.Future;
-
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.Futures;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
/**
* Unit tests for BrokerFacade.
Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
when( dataBroker.beginTransaction() ).thenReturn( mockTransaction );
+ when(mockTransaction.readConfigurationData(any(InstanceIdentifier.class))).thenReturn(
+ ImmutableCompositeNode.builder().toInstance());
mockTransaction.removeConfigurationData( instanceID );
when( mockTransaction.commit() ).thenReturn( expFuture );
Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
when( mockMountInstance.beginTransaction() ).thenReturn( mockTransaction );
+ when(mockTransaction.readConfigurationData(any(InstanceIdentifier.class))).thenReturn(
+ ImmutableCompositeNode.builder().toInstance());
mockTransaction.removeConfigurationData( instanceID );
when( mockTransaction.commit() ).thenReturn( expFuture );
pathArguments.add(new NodeIdentifier(new QName(new URI("augment:augment:module"), "lf112")));
- return new InstanceIdentifier(pathArguments);
+ return InstanceIdentifier.create(pathArguments);
}
private InstanceIdentifier createInstanceIdentifierWithLeafList() throws URISyntaxException {
pathArguments.add(new NodeIdentifier(new QName(new URI("instance:identifier:module"), "cont1")));
pathArguments.add(new NodeWithValue(new QName(new URI("augment:module:leaf:list"), "lflst11"), "lflst11_1"));
- return new InstanceIdentifier(pathArguments);
+ return InstanceIdentifier.create(pathArguments);
}
}
*/
package org.opendaylight.controller.sal.restconf.impl.test;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
Object key;
Object data; // List for a CompositeNode, value Object for a SimpleNode
- NodeData( Object key, Object data ) {
+ NodeData( final Object key, final Object data ) {
this.key = key;
this.data = data;
}
*/
@Test
public void getDataWithSlashesBehindMountPoint() throws UnsupportedEncodingException, URISyntaxException,
- ParseException {
+ ParseException {
InstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
when(
brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
parameters.add(new InstanceIdentifier.NodeIdentifier(qNameCont));
parameters.add(new InstanceIdentifier.NodeIdentifierWithPredicates(qNameList, qNameKeyList,
"GigabitEthernet0/0/0/0"));
- return new InstanceIdentifier(parameters);
+ return InstanceIdentifier.create(parameters);
}
@Test
}
- private Matcher validateOperationsResponseJson(String searchIn, String rpcName, String moduleName) {
+ private Matcher validateOperationsResponseJson(final String searchIn, final String rpcName, final String moduleName) {
StringBuilder regex = new StringBuilder();
regex.append("^");
}
- private Matcher validateOperationsResponseXml(String searchIn, String rpcName, String namespace) {
+ private Matcher validateOperationsResponseXml(final String searchIn, final String rpcName, final String namespace) {
StringBuilder regex = new StringBuilder();
regex.append("^");
assertTrue(
"module1-behind-mount-point in json wasn't found",
prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
- .find());
+ .find());
assertTrue(
"module2-behind-mount-point in json wasn't found",
prepareXmlRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point", responseBody)
- .find());
+ .find());
}
assertTrue(
"module1-behind-mount-point in json wasn't found",
prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
- .find());
+ .find());
split = responseBody.split("<module");
assertEquals("<module element is returned more then once", 2, split.length);
}
- private void validateModulesResponseXml(Response response) {
+ private void validateModulesResponseXml(final Response response) {
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
.find());
}
- private void validateModulesResponseJson(Response response) {
+ private void validateModulesResponseJson(final Response response) {
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
.find());
}
- private Matcher prepareJsonRegex(String module, String revision, String namespace, String searchIn) {
+ private Matcher prepareJsonRegex(final String module, final String revision, final String namespace, final String searchIn) {
StringBuilder regex = new StringBuilder();
regex.append("^");
}
- private Matcher prepareXmlRegex(String module, String revision, String namespace, String searchIn) {
+ private Matcher prepareXmlRegex(final String module, final String revision, final String namespace, final String searchIn) {
StringBuilder regex = new StringBuilder();
regex.append("^");
return ptrn.matcher(searchIn);
}
- private void prepareMockForModulesTest(ControllerContext mockedControllerContext) throws FileNotFoundException {
+ private void prepareMockForModulesTest(final ControllerContext mockedControllerContext) throws FileNotFoundException {
SchemaContext schemaContext = TestUtils.loadSchemaContext("/modules");
mockedControllerContext.setGlobalSchema(schemaContext);
// when(mockedControllerContext.getGlobalSchema()).thenReturn(schemaContext);
}
- private int get(String uri, String mediaType) {
+ private int get(final String uri, final String mediaType) {
return target(uri).request(mediaType).get().getStatus();
}
ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
CompositeNode depth1Cont = toCompositeNode(
- toCompositeNodeData( toNestedQName( "depth1-cont" ),
- toCompositeNodeData( toNestedQName( "depth2-cont1" ),
- toCompositeNodeData( toNestedQName( "depth3-cont1" ),
- toCompositeNodeData( toNestedQName( "depth4-cont1" ),
- toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
- ),
- toCompositeNodeData( toNestedQName( "depth2-cont2" ),
- toCompositeNodeData( toNestedQName( "depth3-cont2" ),
- toCompositeNodeData( toNestedQName( "depth4-cont2" ),
- toSimpleNodeData( toNestedQName( "depth5-leaf2" ), "depth5-leaf2-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth4-leaf2" ), "depth4-leaf2-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth3-leaf2" ), "depth3-leaf2-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth2-leaf1" ), "depth2-leaf1-value" )
- ) );
+ toCompositeNodeData( toNestedQName( "depth1-cont" ),
+ toCompositeNodeData( toNestedQName( "depth2-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont1" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
+ ),
+ toCompositeNodeData( toNestedQName( "depth2-cont2" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont2" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont2" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf2" ), "depth5-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf2" ), "depth4-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf2" ), "depth3-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth2-leaf1" ), "depth2-leaf1-value" )
+ ) );
when( brokerFacade.readConfigurationData( any( InstanceIdentifier.class ) ) )
- .thenReturn( depth1Cont );
+ .thenReturn( depth1Cont );
// Test config with depth 1
Response response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "1" )
- .request( "application/xml" ).get();
+ .request( "application/xml" ).get();
verifyXMLResponse( response, expectEmptyContainer( "depth1-cont" ) );
// Test config with depth 2
response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "2" )
- .request( "application/xml" ).get();
+ .request( "application/xml" ).get();
-// String xml="<depth1-cont><depth2-cont1/><depth2-cont2/><depth2-leaf1>depth2-leaf1-value</depth2-leaf1></depth1-cont>";
-// Response mr=mock(Response.class);
-// when(mr.getEntity()).thenReturn( new java.io.StringBufferInputStream(xml) );
+ // String xml="<depth1-cont><depth2-cont1/><depth2-cont2/><depth2-leaf1>depth2-leaf1-value</depth2-leaf1></depth1-cont>";
+ // Response mr=mock(Response.class);
+ // when(mr.getEntity()).thenReturn( new java.io.StringBufferInputStream(xml) );
verifyXMLResponse( response,
- expectContainer( "depth1-cont",
- expectEmptyContainer( "depth2-cont1" ),
- expectEmptyContainer( "depth2-cont2" ),
- expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
- ) );
+ expectContainer( "depth1-cont",
+ expectEmptyContainer( "depth2-cont1" ),
+ expectEmptyContainer( "depth2-cont2" ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
// Test config with depth 3
response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "3" )
- .request( "application/xml" ).get();
+ .request( "application/xml" ).get();
verifyXMLResponse( response,
- expectContainer( "depth1-cont",
- expectContainer( "depth2-cont1",
- expectEmptyContainer( "depth3-cont1" ),
- expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
- ),
- expectContainer( "depth2-cont2",
- expectEmptyContainer( "depth3-cont2" ),
- expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
- ),
- expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
- ) );
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectEmptyContainer( "depth3-cont1" ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectEmptyContainer( "depth3-cont2" ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
// Test config with depth 4
response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "4" )
- .request( "application/xml" ).get();
+ .request( "application/xml" ).get();
verifyXMLResponse( response,
- expectContainer( "depth1-cont",
- expectContainer( "depth2-cont1",
- expectContainer( "depth3-cont1",
- expectEmptyContainer( "depth4-cont1" ),
- expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
- ),
- expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
- ),
- expectContainer( "depth2-cont2",
- expectContainer( "depth3-cont2",
- expectEmptyContainer( "depth4-cont2" ),
- expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
- ),
- expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
- ),
- expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
- ) );
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectEmptyContainer( "depth4-cont1" ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectEmptyContainer( "depth4-cont2" ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
// Test config with depth 5
response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "5" )
- .request( "application/xml" ).get();
+ .request( "application/xml" ).get();
verifyXMLResponse( response,
- expectContainer( "depth1-cont",
- expectContainer( "depth2-cont1",
- expectContainer( "depth3-cont1",
- expectContainer( "depth4-cont1",
- expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
- ),
- expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
- ),
- expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
- ),
- expectContainer( "depth2-cont2",
- expectContainer( "depth3-cont2",
- expectContainer( "depth4-cont2",
- expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
- ),
- expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
- ),
- expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
- ),
- expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
- ) );
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectContainer( "depth4-cont1",
+ expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
+ ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectContainer( "depth4-cont2",
+ expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
+ ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
// Test config with depth unbounded
response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "unbounded" )
- .request( "application/xml" ).get();
+ .request( "application/xml" ).get();
verifyXMLResponse( response,
- expectContainer( "depth1-cont",
- expectContainer( "depth2-cont1",
- expectContainer( "depth3-cont1",
- expectContainer( "depth4-cont1",
- expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
- ),
- expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
- ),
- expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
- ),
- expectContainer( "depth2-cont2",
- expectContainer( "depth3-cont2",
- expectContainer( "depth4-cont2",
- expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
- ),
- expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
- ),
- expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
- ),
- expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
- ) );
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectContainer( "depth4-cont1",
+ expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
+ ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectContainer( "depth4-cont2",
+ expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
+ ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
// Test operational
CompositeNode depth2Cont1 = toCompositeNode(
- toCompositeNodeData( toNestedQName( "depth2-cont1" ),
- toCompositeNodeData( toNestedQName( "depth3-cont1" ),
- toCompositeNodeData( toNestedQName( "depth4-cont1" ),
- toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
- ),
- toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
- ) );
+ toCompositeNodeData( toNestedQName( "depth2-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont1" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
+ ) );
when( brokerFacade.readOperationalData( any( InstanceIdentifier.class ) ) )
- .thenReturn( depth2Cont1 );
+ .thenReturn( depth2Cont1 );
response = target( "/operational/nested-module:depth1-cont/depth2-cont1" )
- .queryParam( "depth", "3" ).request( "application/xml" ).get();
+ .queryParam( "depth", "3" ).request( "application/xml" ).get();
verifyXMLResponse( response,
- expectContainer( "depth2-cont1",
- expectContainer( "depth3-cont1",
- expectEmptyContainer( "depth4-cont1" ),
- expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
- ),
- expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
- ) );
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectEmptyContainer( "depth4-cont1" ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ) );
}
@Test
paramMap.putSingle( "depth", "1o" );
UriInfo mockInfo = mock( UriInfo.class );
when( mockInfo.getQueryParameters( false ) ).thenAnswer(
- new Answer<MultivaluedMap<String,String>>() {
- @Override
- public MultivaluedMap<String, String> answer( InvocationOnMock invocation ) {
- return paramMap;
- }
- } );
+ new Answer<MultivaluedMap<String,String>>() {
+ @Override
+ public MultivaluedMap<String, String> answer( final InvocationOnMock invocation ) {
+ return paramMap;
+ }
+ } );
getDataWithInvalidDepthParameterTest( mockInfo );
getDataWithInvalidDepthParameterTest( mockInfo );
}
- private void getDataWithInvalidDepthParameterTest( UriInfo uriInfo ) {
+ private void getDataWithInvalidDepthParameterTest( final UriInfo uriInfo ) {
try {
restconfImpl.readConfigurationData( "nested-module:depth1-cont", uriInfo );
fail( "Expected RestconfDocumentedException" );
}
catch( RestconfDocumentedException e ) {
assertTrue( "Unexpected error message: " + e.getErrors().get( 0 ).getErrorMessage(),
- e.getErrors().get( 0 ).getErrorMessage().contains( "depth" ) );
+ e.getErrors().get( 0 ).getErrorMessage().contains( "depth" ) );
}
}
- private void verifyXMLResponse( Response response, NodeData nodeData ) {
+ private void verifyXMLResponse( final Response response, final NodeData nodeData ) {
Document doc = TestUtils.loadDocumentFrom( (InputStream) response.getEntity() );
assertNotNull( "Could not parse XML document", doc );
}
@SuppressWarnings("unchecked")
- private void verifyContainerElement( Element element, NodeData nodeData ) {
+ private void verifyContainerElement( final Element element, final NodeData nodeData ) {
assertEquals( "Element local name", nodeData.key, element.getNodeName() );
NodeList childNodes = element.getChildNodes();
if( nodeData.data == null ) { // empty container
assertTrue( "Expected no child elements for \"" + element.getNodeName() + "\"",
- childNodes.getLength() == 0 );
+ childNodes.getLength() == 0 );
return;
}
Element actualElement = (Element)actualChild;
NodeData expChild = expChildMap.remove( actualElement.getNodeName() );
assertNotNull( "Unexpected child element for parent \"" + element.getNodeName() +
- "\": " + actualElement.getNodeName(), expChild );
+ "\": " + actualElement.getNodeName(), expChild );
if( expChild.data == null || expChild.data instanceof List ) {
verifyContainerElement( actualElement, expChild );
}
else {
assertEquals( "Text content for element: " + actualElement.getNodeName(),
- expChild.data, actualElement.getTextContent() );
+ expChild.data, actualElement.getTextContent() );
}
}
if( !expChildMap.isEmpty() ) {
fail( "Missing elements for parent \"" + element.getNodeName() +
- "\": " + expChildMap.keySet() );
+ "\": " + expChildMap.keySet() );
}
}
- private NodeData expectContainer( String name, NodeData... childData ) {
+ private NodeData expectContainer( final String name, final NodeData... childData ) {
return new NodeData( name, Lists.newArrayList( childData ) );
}
- private NodeData expectEmptyContainer( String name ) {
+ private NodeData expectEmptyContainer( final String name ) {
return new NodeData( name, null );
}
- private NodeData expectLeaf( String name, Object value ) {
+ private NodeData expectLeaf( final String name, final Object value ) {
return new NodeData( name, value );
}
- private QName toNestedQName( String localName ) {
+ private QName toNestedQName( final String localName ) {
return QName.create( "urn:nested:module", "2014-06-3", localName );
}
@SuppressWarnings("unchecked")
- private CompositeNode toCompositeNode( NodeData nodeData ) {
+ private CompositeNode toCompositeNode( final NodeData nodeData ) {
CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
builder.setQName( (QName) nodeData.key );
return builder.toInstance();
}
- private NodeData toCompositeNodeData( QName key, NodeData... childData ) {
+ private NodeData toCompositeNodeData( final QName key, final NodeData... childData ) {
return new NodeData( key, Lists.newArrayList( childData ) );
}
- private NodeData toSimpleNodeData( QName key, Object value ) {
+ private NodeData toSimpleNodeData( final QName key, final Object value ) {
return new NodeData( key, value );
}
}
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import com.google.common.collect.Iterables;
+
import java.io.FileNotFoundException;
import java.util.Set;
initMountService(true);
InstanceIdWithSchemaNode instanceIdentifier = controllerContext
.toInstanceIdentifier("simple-nodes:users/yang-ext:mount/");
- assertEquals(true, instanceIdentifier.getInstanceIdentifier().getPath().isEmpty());
+ assertTrue(Iterables.isEmpty(instanceIdentifier.getInstanceIdentifier().getPathArguments()));
}
@Test
.toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class");
}
- public void initMountService(boolean withSchema) {
+ public void initMountService(final boolean withSchema) {
MountService mountService = mock(MountService.class);
controllerContext.setMountService(mountService);
BrokerFacade brokerFacade = mock(BrokerFacade.class);
Set<Module> modules2 = TestUtils.loadModulesFrom("/test-config-data/yang2");
SchemaContext schemaContext2 = TestUtils.loadSchemaContext(modules2);
MountInstance mountInstance = mock(MountInstance.class);
- if (withSchema)
+ if (withSchema) {
when(mountInstance.getSchemaContext()).thenReturn(schemaContext2);
- else
+ } else {
when(mountInstance.getSchemaContext()).thenReturn(null);
+ }
when(mountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
}
}
import java.util.List;
import java.util.Map;
import java.util.Set;
-
+import org.apache.commons.lang3.BooleanUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
private static final String NUMBER = "number";
private static final String BOOLEAN = "boolean";
private static final String STRING = "string";
+ private static final String ID_KEY = "id";
+ private static final String SUB_TYPES_KEY = "subTypes";
private static final Map<Class<? extends TypeDefinition<?>>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING;
JSONObject models = new JSONObject();
processContainers(module, models);
processRPCs(module, models);
+ processIdentities(module, models);
return models;
}
}
}
+ /**
+ * Processes the 'identity' statement in a yang model
+ * and maps it to a 'model' in the Swagger JSON spec.
+ *
+ * @param module The module from which the identity stmt will be processed
+ * @param models The JSONObject in which the parsed identity will be put as a 'model' obj
+ * @throws JSONException
+ */
+ private void processIdentities(Module module, JSONObject models) throws JSONException {
+
+ String moduleName = module.getName();
+ Set<IdentitySchemaNode> idNodes = module.getIdentities();
+ _logger.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
+
+ for(IdentitySchemaNode idNode : idNodes){
+ JSONObject identityObj=new JSONObject();
+ String identityName = idNode.getQName().getLocalName();
+ _logger.debug("Processing Identity: {}", identityName);
+
+ identityObj.put(ID_KEY, identityName);
+ identityObj.put(DESCRIPTION_KEY, idNode.getDescription());
+
+ JSONObject props = new JSONObject();
+ IdentitySchemaNode baseId = idNode.getBaseIdentity();
+
+
+ if(baseId==null) {
+ /**
+ * This is a base identity. So lets see if
+ * it has sub types. If it does, then add them to the model definition.
+ */
+ Set<IdentitySchemaNode> derivedIds = idNode.getDerivedIdentities();
+
+ if(derivedIds != null) {
+ JSONArray subTypes = new JSONArray();
+ for(IdentitySchemaNode derivedId : derivedIds){
+ subTypes.put(derivedId.getQName().getLocalName());
+ }
+ identityObj.put(SUB_TYPES_KEY, subTypes);
+ }
+ } else {
+ /**
+ * This is a derived entity. Add it's base type & move on.
+ */
+ props.put(TYPE_KEY, baseId.getQName().getLocalName());
+ }
+
+ //Add the properties. For a base type, this will be an empty object as required by the Swagger spec.
+ identityObj.put(PROPERTIES_KEY, props);
+ models.put(identityName, identityObj);
+ }
+ }
/**
* Processes the container node and populates the moduleJSON
*
if (node instanceof LeafSchemaNode) {
property = processLeafNode((LeafSchemaNode) node);
} else if (node instanceof ListSchemaNode) {
- property = processListSchemaNode((ListSchemaNode) node, moduleName, models);
+ property = processListSchemaNode((ListSchemaNode) node, moduleName, models, isConfig);
} else if (node instanceof LeafListSchemaNode) {
property = processLeafListNode((LeafListSchemaNode) node);
*
* @param listNode
* @param moduleName
+ * @param isConfig
* @return
* @throws JSONException
* @throws IOException
*/
private JSONObject processListSchemaNode(ListSchemaNode listNode, String moduleName,
- JSONObject models) throws JSONException, IOException {
+ JSONObject models, Boolean isConfig) throws JSONException, IOException {
Set<DataSchemaNode> listChildren = listNode.getChildNodes();
- String fileName = listNode.getQName().getLocalName();
+ String fileName = (BooleanUtils.isNotFalse(isConfig)?OperationBuilder.CONFIG:OperationBuilder.OPERATIONAL) +
+ listNode.getQName().getLocalName();
JSONObject childSchemaProperties = processChildren(listChildren, moduleName, models);
JSONObject childSchema = getSchemaTemplate();
processUnionType((UnionTypeDefinition) leafTypeDef, property);
} else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
- property.putOpt(TYPE_KEY, "object");
+ property.putOpt(TYPE_KEY, ((IdentityrefTypeDefinition) leafTypeDef).getIdentity().getQName().getLocalName());
} else if (leafTypeDef instanceof BinaryTypeDefinition) {
processBinaryType((BinaryTypeDefinition) leafTypeDef, property);
} else {
private void processUnionType(UnionTypeDefinition unionType, JSONObject property)
throws JSONException {
- List<TypeDefinition<?>> unionTypes = unionType.getTypes();
- JSONArray unionArray = new JSONArray();
- for (TypeDefinition<?> typeDef : unionTypes) {
- unionArray.put(YANG_TYPE_TO_JSON_TYPE_MAPPING.get(typeDef.getClass()));
+ StringBuilder type = new StringBuilder();
+ for (TypeDefinition<?> typeDef : unionType.getTypes() ) {
+ if( type.length() > 0 ){
+ type.append( " or " );
+ }
+ type.append(YANG_TYPE_TO_JSON_TYPE_MAPPING.get(typeDef.getClass()));
}
- property.put(TYPE_KEY, unionArray);
+
+ property.put(TYPE_KEY, type );
}
/**
private final Map<InstanceIdentifier, Long> instanceIdToLongId = new TreeMap<>(
new Comparator<InstanceIdentifier>() {
@Override
- public int compare(InstanceIdentifier o1, InstanceIdentifier o2) {
+ public int compare(final InstanceIdentifier o1, final InstanceIdentifier o2) {
return o1.toString().compareToIgnoreCase(o2.toString());
}
});
return urlToId;
}
- public void setGlobalSchema(SchemaService globalSchema) {
+ public void setGlobalSchema(final SchemaService globalSchema) {
this.globalSchema = globalSchema;
}
- private String findModuleName(InstanceIdentifier id, SchemaContext context) {
- PathArgument rootQName = id.getPath().get(0);
+ private String findModuleName(final InstanceIdentifier id, final SchemaContext context) {
+ PathArgument rootQName = id.getPathArguments().iterator().next();
for (Module mod : context.getModules()) {
if (mod.getDataChildByName(rootQName.getNodeType()) != null) {
return mod.getName();
return null;
}
- private String generateUrlPrefixFromInstanceID(InstanceIdentifier key, String moduleName) {
- List<PathArgument> path = key.getPath();
+ private String generateUrlPrefixFromInstanceID(final InstanceIdentifier key, final String moduleName) {
StringBuilder builder = new StringBuilder();
if (moduleName != null) {
builder.append(moduleName);
- builder.append(":");
+ builder.append(':');
}
boolean first = true;
- for (PathArgument arg : path) {
+ for (PathArgument arg : key.getPathArguments()) {
String name = arg.getNodeType().getLocalName();
if (first) {
first = false;
} else {
- builder.append("/");
+ builder.append('/');
}
builder.append(name);
if (arg instanceof InstanceIdentifier.NodeIdentifierWithPredicates) {
NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg;
for (Entry<QName, Object> entry : nodeId.getKeyValues().entrySet()) {
- builder.append("/").append(entry.getValue());
+ builder.append('/').append(entry.getValue());
}
}
}
- return builder.append("/").toString();
+ return builder.append('/').toString();
}
- private String getYangMountUrl(InstanceIdentifier key) {
+ private String getYangMountUrl(final InstanceIdentifier key) {
String modName = findModuleName(key, globalSchema.getGlobalContext());
return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount/";
}
- public ResourceList getResourceList(UriInfo uriInfo, Long id) {
+ public ResourceList getResourceList(final UriInfo uriInfo, final Long id) {
InstanceIdentifier iid = getInstanceId(id);
if (iid == null) {
return null; // indicating not found.
return list;
}
- private InstanceIdentifier getInstanceId(Long id) {
+ private InstanceIdentifier getInstanceId(final Long id) {
InstanceIdentifier instanceId;
synchronized (lock) {
instanceId = longIdToInstanceId.get(id);
return instanceId;
}
- private SchemaContext getSchemaContext(InstanceIdentifier id) {
+ private SchemaContext getSchemaContext(final InstanceIdentifier id) {
if (id == null) {
return null;
return context;
}
- public ApiDeclaration getMountPointApi(UriInfo uriInfo, Long id, String module, String revision) {
+ public ApiDeclaration getMountPointApi(final UriInfo uriInfo, final Long id, final String module, final String revision) {
InstanceIdentifier iid = getInstanceId(id);
SchemaContext context = getSchemaContext(iid);
String urlPrefix = getYangMountUrl(iid);
return super.getApiDeclaration(module, revision, uriInfo, context, urlPrefix);
}
- private ApiDeclaration generateDataStoreApiDoc(UriInfo uriInfo, String context) {
+ private ApiDeclaration generateDataStoreApiDoc(final UriInfo uriInfo, final String context) {
ApiDeclaration declaration = super.createApiDeclaration(createBasePathFromUriInfo(uriInfo));
List<Api> apis = new LinkedList<>();
}
- private Api createGetApi(String datastore, String note, String context) {
+ private Api createGetApi(final String datastore, final String note, final String context) {
Operation getConfig = new Operation();
getConfig.setMethod("GET");
getConfig.setNickname("GET " + datastore);
return api;
}
- public void setMountService(MountProvisionService mountService) {
+ public void setMountService(final MountProvisionService mountService) {
this.mountService = mountService;
}
@Override
- public void onMountPointCreated(InstanceIdentifier path) {
+ public void onMountPointCreated(final InstanceIdentifier path) {
synchronized (lock) {
Long idLong = idKey.incrementAndGet();
instanceIdToLongId.put(path, idLong);
}
@Override
- public void onMountPointRemoved(InstanceIdentifier path) {
+ public void onMountPointRemoved(final InstanceIdentifier path) {
synchronized (lock) {
Long id = instanceIdToLongId.remove(path);
longIdToInstanceId.remove(id);
<script src='swagger-ui.js' type='text/javascript'></script>\r
<script src='lib/odl/list_mounts.js' type='text/javascript'></script>\r
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>\r
-<script src='lib/odl/rest-tree.js' type='text/javascript'></script>\r
<script src='lib/odl/swagger.js' type='text/javascript'></script>\r
\r
<script type="text/javascript">\r
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
}
}
+ @Test
+ public void testEdgeCases() throws Exception {
+ Preconditions.checkArgument(helper.getModules() != null, "No modules found");
+
+ for (Entry<File, Module> m : helper.getModules().entrySet()) {
+ if (m.getKey().getAbsolutePath().endsWith("toaster.yang")) {
+ ApiDeclaration doc = generator.getSwaggerDocSpec(m.getValue(),
+ "http://localhost:8080/restconf", "");
+ Assert.assertNotNull(doc);
+
+ //testing bugs.opendaylight.org bug 1290. UnionType model type.
+ String jsonString = doc.getModels().toString();
+ assertTrue(
+ jsonString.contains( "testUnion\":{\"type\":\"integer or string\",\"required\":false}" ) );
+ }
+ }
+ }
+
private void validateToaster(ApiDeclaration doc) throws Exception {
Set<String> expectedUrls = new TreeSet<>(Arrays.asList(new String[] {
"/config/toaster2:toaster/", "/operational/toaster2:toaster/",
expectedConfigMethods.removeAll(actualConfigMethods);
fail("Missing expected method on config API: " + expectedConfigMethods);
}
+
// TODO: we should really do some more validation of the
// documentation...
/**
</rpc-registry>
<data-broker>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<name>binding-data-broker</name>
</data-broker>
*/
package org.opendaylight.controller.config.yang.config.toaster_provider.impl;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sample.toaster.provider.OpendaylightToaster;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
/**
*
*/
-public final class ToasterProviderModule extends org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule
- {
+public final class ToasterProviderModule extends
+ org.opendaylight.controller.config.yang.config.toaster_provider.impl.AbstractToasterProviderModule {
private static final Logger log = LoggerFactory.getLogger(ToasterProviderModule.class);
- public ToasterProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ public ToasterProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final 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) {
+ public ToasterProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final ToasterProviderModule oldModule, final 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
+ // No need to validate dependencies, since all dependencies have
+ // mandatory true flag in yang
// config-subsystem will perform the validation for dependencies
}
// Register to md-sal
opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
- DataProviderService dataBrokerService = getDataBrokerDependency();
+ DataBroker dataBrokerService = getDataBrokerDependency();
opendaylightToaster.setDataProvider(dataBrokerService);
- final ListenerRegistration<DataChangeListener> dataChangeListenerRegistration =
- dataBrokerService.registerDataChangeListener( OpendaylightToaster.TOASTER_IID, opendaylightToaster );
+ final ListenerRegistration<DataChangeListener> dataChangeListenerRegistration = dataBrokerService
+ .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, OpendaylightToaster.TOASTER_IID,
+ opendaylightToaster, DataChangeScope.SUBTREE);
final BindingAwareBroker.RpcRegistration<ToasterService> rpcRegistration = getRpcRegistryDependency()
.addRpcImplementation(ToasterService.class, opendaylightToaster);
import java.util.concurrent.atomic.AtomicLong;
import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
-import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput;
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.Toaster.ToasterStatus;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterBuilder;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterOutOfBreadBuilder;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestockedBuilder;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final DisplayString TOASTER_MODEL_NUMBER = new DisplayString("Model 1 - Binding Aware");
private NotificationProviderService notificationProvider;
- private DataBrokerService dataProvider;
+ private DataBroker dataProvider;
private final ExecutorService executor;
executor = Executors.newFixedThreadPool(1);
}
- public void setNotificationProvider(NotificationProviderService salService) {
+ public void setNotificationProvider(final NotificationProviderService salService) {
this.notificationProvider = salService;
}
- public void setDataProvider(DataBrokerService salDataProvider) {
+ public void setDataProvider(final DataBroker salDataProvider) {
this.dataProvider = salDataProvider;
updateStatus();
}
executor.shutdown();
if (dataProvider != null) {
- final DataModificationTransaction t = dataProvider.beginTransaction();
- t.removeOperationalData(TOASTER_IID);
- t.commit().get();
+ WriteTransaction t = dataProvider.newWriteOnlyTransaction();
+ t.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID);
+ t.commit().get(); // FIXME: This call should not be blocking.
}
}
* Implemented from the DataChangeListener interface.
*/
@Override
- public void onDataChanged( DataChangeEvent<InstanceIdentifier<?>, DataObject> change ) {
- DataObject dataObject = change.getUpdatedConfigurationData().get( TOASTER_IID );
+ public void onDataChanged( final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change ) {
+ DataObject dataObject = change.getUpdatedSubtree();
if( dataObject instanceof Toaster )
{
Toaster toaster = (Toaster) dataObject;
* RestConf RPC call implemented from the ToasterService interface.
*/
@Override
- public Future<RpcResult<Void>> makeToast(MakeToastInput input) {
+ public Future<RpcResult<Void>> makeToast(final MakeToastInput input) {
LOG.info("makeToast: " + input);
synchronized (taskLock) {
* ToasterRestocked notification.
*/
@Override
- public Future<RpcResult<java.lang.Void>> restockToaster(RestockToasterInput input) {
+ public Future<RpcResult<java.lang.Void>> restockToaster(final RestockToasterInput input) {
LOG.info( "restockToaster: " + input );
synchronized( taskLock ) {
private void updateStatus() {
if (dataProvider != null) {
- final DataModificationTransaction t = dataProvider.beginTransaction();
- t.removeOperationalData(TOASTER_IID);
- t.putOperationalData(TOASTER_IID, buildToaster());
+ WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
+ tx.put(LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster());
try {
- t.commit().get();
+ tx.commit().get();
} catch (InterruptedException | ExecutionException e) {
LOG.warn("Failed to update toaster status, operational otherwise", e);
}
final MakeToastInput toastRequest;
- public MakeToastTask(MakeToastInput toast) {
+ public MakeToastTask(final MakeToastInput toast) {
toastRequest = toast;
}
uses config:service-ref {
refine type {
mandatory false;
- config:required-identity mdsal:binding-data-broker;
+ config:required-identity mdsal:binding-async-data-broker;
}
}
}
Set<Capability> capabilities = new HashSet<>();
// [RFC6241] 8.3. Candidate Configuration Capability
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
+
+ // TODO rollback on error not supported EditConfigXmlParser:100
// [RFC6241] 8.5. Rollback-on-Error Capability
- capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
+ // capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
Set<Module> modules = yangStoreSnapshot.getModules();
for (Module module : modules) {
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
import com.google.common.collect.Maps;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
public class YangStoreSnapshotImpl implements YangStoreSnapshot {
private static final Logger logger = LoggerFactory.getLogger(YangStoreSnapshotImpl.class);
private final Map<String /* Namespace from yang file */,
- Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
private final Map<QName, Map<String, ModuleMXBeanEntry>> qNamesToIdentitiesToModuleMXBeanEntries;
private final SchemaContext schemaContext;
- public YangStoreSnapshotImpl(SchemaContext resolveSchemaContext) {
+ public YangStoreSnapshotImpl(final SchemaContext resolveSchemaContext) {
logger.trace("Resolved modules:{}", resolveSchemaContext.getModules());
this.schemaContext = resolveSchemaContext;
// JMX generator
qNamesToSIEs.put(sieEntry.getKey(), sieEntry.getValue());
} else {
throw new IllegalStateException("Cannot add two SIE with same qname "
- + sieEntry.getValue());
+ + sieEntry.getValue());
}
}
}
TypeProviderWrapper typeProviderWrapper = new TypeProviderWrapper(
new TypeProviderImpl(resolveSchemaContext));
- QName qName = new QName(module.getNamespace(), module.getRevision(), module.getName());
+ QName qName = QName.create(module.getNamespace(), module.getRevision(), module.getName());
Map<String /* MB identity local name */, ModuleMXBeanEntry> namesToMBEs =
Collections.unmodifiableMap(ModuleMXBeanEntry.create(module, qNamesToSIEs, resolveSchemaContext,
}
@Override
- public String getModuleSource(org.opendaylight.yangtools.yang.model.api.ModuleIdentifier moduleIdentifier) {
+ public String getModuleSource(final org.opendaylight.yangtools.yang.model.api.ModuleIdentifier moduleIdentifier) {
return schemaContext.getModuleSource(moduleIdentifier).get();
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.matchers.JUnitMatchers;
-import org.opendaylight.controller.config.api.LookupRegistry;
-import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
-import org.opendaylight.yangtools.yang.common.QName;
import java.net.URI;
import java.text.ParseException;
import java.util.Map;
import java.util.Set;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.opendaylight.controller.config.api.LookupRegistry;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.yangtools.yang.common.QName;
public class NetconfOperationServiceImplTest {
Assert.assertThat(
message,
JUnitMatchers
- .containsString("missing from config subsystem but present in yangstore: [(namespace?revision=1970-01-01)qname2]"));
+ .containsString("missing from config subsystem but present in yangstore: [(namespace?revision=1970-01-01)qname2]"));
Assert.assertThat(
message,
JUnitMatchers
- .containsString("All modules present in config: [(namespace?revision=1970-01-01)qname1]"));
+ .containsString("All modules present in config: [(namespace?revision=1970-01-01)qname1]"));
}
}
- private YangStoreSnapshot mockYangStoreSnapshot(String... qnames) {
+ private YangStoreSnapshot mockYangStoreSnapshot(final String... qnames) {
YangStoreSnapshot mock = mock(YangStoreSnapshot.class);
Map<String, Map<String, ModuleMXBeanEntry>> map = Maps.newHashMap();
return mock;
}
- private ModuleMXBeanEntry mockMBeanEntry(String qname) {
+ private ModuleMXBeanEntry mockMBeanEntry(final String qname) {
ModuleMXBeanEntry mock = mock(ModuleMXBeanEntry.class);
QName q = getQName(qname);
doReturn(q).when(mock).getYangModuleQName();
return mock;
}
- private QName getQName(String qname) {
- return new QName(URI.create("namespace"), date1970_01_01, qname);
+ private QName getQName(final String qname) {
+ return QName.create(URI.create("namespace"), date1970_01_01, qname);
}
- private LookupRegistry mockJmxClient(String... visibleQNames) {
+ private LookupRegistry mockJmxClient(final String... visibleQNames) {
LookupRegistry mock = mock(LookupRegistry.class);
Set<String> qnames = Sets.newHashSet();
for (String visibleQName : visibleQNames) {
package org.opendaylight.controller.netconf.persist.impl;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.Collections2;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
-
import javax.annotation.concurrent.Immutable;
-
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.Collections2;
-
@Immutable
public class ConfigPusher {
private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class);
ConflictingVersionException lastException;
Stopwatch stopwatch = new Stopwatch().start();
do {
- try (NetconfOperationService operationService = getOperationServiceWithRetries(configSnapshotHolder.getCapabilities(), configSnapshotHolder.toString())) {
+ String idForReporting = configSnapshotHolder.toString();
+ SortedSet<String> expectedCapabilities = checkNotNull(configSnapshotHolder.getCapabilities(),
+ "Expected capabilities must not be null - %s, check %s", idForReporting,
+ configSnapshotHolder.getClass().getName());
+ try (NetconfOperationService operationService = getOperationServiceWithRetries(expectedCapabilities, idForReporting)) {
return pushConfig(configSnapshotHolder, operationService);
} catch (ConflictingVersionException e) {
lastException = e;
private static NetconfMessage createEditConfigMessage(Element dataElement) throws NetconfDocumentedException {
String editConfigResourcePath = "/netconfOp/editConfig.xml";
try (InputStream stream = ConfigPersisterNotificationHandler.class.getResourceAsStream(editConfigResourcePath)) {
- Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcePath);
+ checkNotNull(stream, "Unable to load resource " + editConfigResourcePath);
Document doc = XmlUtil.readXmlToDocument(stream);
private static NetconfMessage getCommitMessage() {
String resource = "/netconfOp/commit.xml";
try (InputStream stream = ConfigPusher.class.getResourceAsStream(resource)) {
- Preconditions.checkNotNull(stream, "Unable to load resource " + resource);
+ checkNotNull(stream, "Unable to load resource " + resource);
return new NetconfMessage(XmlUtil.readXmlToDocument(stream));
} catch (SAXException | IOException e) {
// error reading the xml file bundled into the jar
*/
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;
+import org.junit.matchers.JUnitMatchers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
final class TestingExceptionHandler implements Thread.UncaughtExceptionHandler {
+ private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterTest.class);
+
private Throwable t;
@Override
public void uncaughtException(Thread t, Throwable e) {
+ logger.debug("Uncaught exception in thread {}", t, e);
this.t = e;
}
--- /dev/null
+usage:
+Submit address + port for initial TCP connection (PURE TCP CONNECTIONS ARE NOT SUPPORTED YET)
+Submit username + password in addition to address + port for initial SSH connection
+If no arguments(or unexpected combination) is submitted, cli will be started without initial connection
+To use with ODL controller, run with: java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar --server localhost --port 1830 --username admin --password admin
+
+Generic cli for netconf devices
+
+optional arguments:
+ -h, --help show this help message and exit
+
+TCP:
+ Base arguments to initiate TCP connection right away
+
+ --server SERVER Netconf device ip-address/domain name
+ --port PORT Netconf device port
+
+SSH:
+ SSH credentials, if provided, initial connection will be attempted using SSH
+
+ --username USERNAME Username for SSH connection
+ --password PASSWORD Password for SSH connection
+------------------------------------------------------------------------
+
+To run the cli execute:
+
+java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar --username user --password password --server serverIP --port optionalPort
+
+The cli will connect to the remote device automatically.
+The initialization may take a few moments depending on the size of schemas provided by the device.
+To view the progress, one can take a look inside netconfcli.log file (All logs are in this file starting with level TRACE).
+Cli does not print any logging messages to the console, only to the file.
+
+------------------------------------------------------------------------
+
+Correct initialization + connection should display following output:
+
+[maros@localhost target]$ java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar --server localhost --port 1830 --username admin --password admin
+Connecting to localhost via SSH. Please wait.
+Cli is up, available commands:
+
+add-flow(sal-flow) add-group(sal-group)
+add-meter(sal-meter) begin-transaction(sal-remote)
+cancel-commit(ietf-netconf) cancel-toast(toaster)
+clear-toasts-made(toaster-provider-impl) close(netconf-cli)
+close-session(ietf-netconf) commit(ietf-netconf)
+connect(netconf-cli) copy-config(ietf-netconf)
+create-data-change-event-subscription(sal-remote) create-notification-stream(sal-remote)
+delete-config(ietf-netconf) discard-changes(ietf-netconf)
+disconnect(netconf-cli) edit-config(ietf-netconf)
+finish-transaction(flow-capable-transaction) get(ietf-netconf)
+get-aggregate-flow-statistics-from-flow-table-for-all-flows(opendaylight-flow-statistics) get-aggregate-flow-statistics-from-flow-table-for-given-match(opendaylight-flow-statistics)
+get-all-flow-statistics-from-flow-table(opendaylight-flow-statistics) get-all-flows-statistics-from-all-flow-tables(opendaylight-flow-statistics)
+get-all-group-statistics(opendaylight-group-statistics) get-all-meter-config-statistics(opendaylight-meter-statistics)
+get-all-meter-statistics(opendaylight-meter-statistics) get-all-node-connectors-statistics(opendaylight-port-statistics)
+get-all-queues-statistics-from-all-ports(opendaylight-queue-statistics) get-all-queues-statistics-from-given-port(opendaylight-queue-statistics)
+get-config(ietf-netconf) get-dead-events-count(threadpool-impl)
+get-flow-statistics-from-flow-table(opendaylight-flow-statistics) get-flow-tables-statistics(opendaylight-flow-table-statistics)
+get-group-description(opendaylight-group-statistics) get-group-features(opendaylight-group-statistics)
+get-group-statistics(opendaylight-group-statistics) get-meter-features(opendaylight-meter-statistics)
+get-meter-statistics(opendaylight-meter-statistics) get-next-transaction-id(flow-capable-transaction)
+get-node-connector-statistics(opendaylight-port-statistics) get-queue(sal-queue)
+get-queue-statistics-from-given-port(opendaylight-queue-statistics) get-schema(ietf-netconf-monitoring)
+help(netconf-cli) kill-session(ietf-netconf)
+lock(ietf-netconf) make-scrambled-with-wheat(kitchen-service-impl)
+make-toast(toaster) remove-flow(sal-flow)
+remove-group(sal-group) remove-meter(sal-meter)
+reset(config-logging) restock-toaster(toaster)
+shutdown(shutdown-impl) solicit-refresh(flow-topology-discovery)
+transmit-packet(packet-processing) unlock(ietf-netconf)
+update-flow(sal-flow) update-group(sal-group)
+update-meter(sal-meter) update-port(sal-port)
+update-table(sal-table) validate(ietf-netconf)
+
+netconf()>
+
+
+------------------------------------------------------------------------
+
+At this stage, any supported rpc can be invoked. To see all possible rpcs press TAB (serves as autocomplete). The output contains all the commands reported after at start-up
+
+------------------------------------------------------------------------
+
+Example step-by-step execution of get-config rpc:
+
+1. Type get-config, hit TAB, hit enter
+2. Cli will now walk all the input arguments of get-config rpc and ask for value
+3. Cli asks for filter value
+4. Submit filter (using TAB autocomplete) as a schema path or type "skip" (to not add any filter) and hit enter
+5. Cli asks for config source (e.g. which data-store to query)
+6. Use TAB to view options and submit either running or candidate data-store
+7. Cli will display configuration of the remote device e.g. :
+
+data {
+ a {
+ address {
+ last-name a
+ first-name o
+ street aaaaa
+ }
+ address {
+ last-name a
+ first-name t
+ }
+ address {
+ last-name a
+ first-name y
+ }
+ }
+}
+
+
+------------------------------------------------------------------------
--- /dev/null
+This file contains ODL controller specific examples:
+
+1A. Connecting to ODL controller automatically:
+ a. Make sure ODL controller is running on your or any other accessible device
+ b. Start the cli using this command (in folder controller/opendaylight/netconf/netconf-cli/target/):
+ java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar --server localhost --port 1830 --username admin --password admin
+ c. The cli will start up in aprox. 20 seconds (Schema download might take some time on the first connection, subsequent attempts should take less time)
+ d. You should see the list of commands avaliable in the controller e.g.:
+ add-flow(sal-flow) add-group(sal-group)
+ add-meter(sal-meter) begin-transaction(sal-remote)
+ cancel-commit(ietf-netconf) cancel-toast(toaster)
+ clear-toasts-made(toaster-provider-impl) close(netconf-cli)
+ close-session(ietf-netconf) commit(ietf-netconf)
+ connect(netconf-cli) copy-config(ietf-netconf)
+ create-data-change-event-subscription(sal-remote) ....
+
+
+1B. Connecting to ODL from the CLI:
+ a. Make sure ODL controller is running on your or any other accessible device
+ b. Start the cli using this command (in folder controller/opendaylight/netconf/netconf-cli/target/):
+ java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar
+ c. The cli will start app right away (few seconds)
+ d. You should see only the basic commands e.g. connect, close, help, disconnect
+ e. Type connect, hit TAB, hit ENTER
+ f. Cli will ask for connect arguments: [address-name, address-port, user-name, user-password]
+ g. Address-name
+ The cli will ask what type of address you want to provide (domain-name or ip-address). This is caused by the yang model for connect command, the address-name is of type ietf-inet-types:host, which is a union of domain-name and ip-address.
+ Submit "domain-name" (TAB can be used for autocompete)
+ Now you need to provide value, submit "localhost" (TAB can be used for autocomplete, as "localhost" is the default value)
+ h. Address-port
+ Submit 1830 (default port for netconf SSH server in ODL)
+ i. User-name
+ Submit "admin"
+ j. User-password
+ Submit "admin"
+ k. The connection will be up in aprox. 20 seconds (Schema download might take some time on the first connection, subsequent attempts should take less time)
+ l. You should see the list of commands available in the controller
+
+
+2. Disconnecting from ODL in the CLI
+ a. Execute scenario 1A or 1B
+ b. Type "disconn", hit TAB, hit Enter
+ c. You should see the following output:
+ status Connection disconnected
+ d. Use TAB to see available commands, only local commands are present now
+ e. Now you can use the connect command(as in 1B) to connect again
+
+
+3. Using help command
+ a. Help command can be executed in connected as well as disconnected state
+ b. Type "help", hit TAB, hit Enter
+ c. You should see the help conent containing the list of all available commands with description for each of them e.g.
+ commands {
+ commands [id=close(netconf-cli)] {
+ id close(netconf-cli)
+ description Close the whole cli
+ }
+ commands [id=help(netconf-cli)] {
+ id help(netconf-cli)
+ description Display help
+ }
+ commands [id=disconnect(netconf-cli)] {
+ id disconnect(netconf-cli)
+ description Disconnect from a netconf device that is currently connected
+ }
+ commands [id=connect(netconf-cli)] {
+ id connect(netconf-cli)
+ description Connect to a remote netconf device, if not connected yet. Connection initialization is blocking and might take some time, depending on amount of yang schemas in remote device.
+ }
+ }
+
+
+4. Executing get-config command (get-config(ietf-netconf))
+ a. Execute scenario 1A or 1B
+ b. Type "get-config", hit TAB, hit Enter
+ c. Cli will ask for get-config arguments: [filter, source]
+ d. Filter
+ Submit "skip" (This will ignore the filter attribute, ODL does not support filtering at this moment, but will be supported in near future)
+ e. Source
+ You have to choose from candidate, running, startup. Submit running.
+ f. You should see the whole configuration of the ODL e.g.:
+ data {
+ modules {
+ module {
+ module [name=toaster-provider-impl] {
+ name toaster-provider-impl
+ type (urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?revision=2014-01-31)toaster-provider-impl
+ choice configuration (toaster-provider-impl) {
+ ...
+
+
+5. Executing get command (get(ietf-netconf))
+ a. Execute scenario 1A or 1B
+ b. Type "get(", hit TAB, hit Enter
+ c. Cli will ask for get arguments: [filter]
+ d. Filter
+ Submit "skip" (This will ignore the filter attribute, ODL does not support filtering at this moment, but will be supported in near future)
+ f. You should see the whole data-tree of the ODL
+
+
+6. Executing edit-config command (edit-config(ietf-netconf))
+ a. Execute scenario 1A or 1B
+ b. Type "edit", hit TAB, hit Enter
+ c. Cli will ask for edit-config arguments: [default-operation, edit-content, error-option, target, test-option]
+ d. Config
+ Config contains the data to be edited
+ 1. First you have to specify a path pointing to a concrete data node. Use TAB to help with autocomplete.
+ Submit "modules(config)/module(config)/"
+ Module node is of type list and now you have to construct a whole new list entry for the module list.
+ 2. The cli will ask for these nodes: [configuration, name, type]
+ Name - Submit any name e.g. newModule
+ Type - For Type you have to pick from available module types in the ODL, hit TAB for hints
+ Submit "threadfactory-naming(threadpool-impl)" to create a new instance of threadfactory in the ODL.
+ Configuration - For configuration you have to pick from available module types again
+ Submit "threadfactory-naming" to match previous module type
+ The cli will now ask for threadfactory-naming specific configuration: [prefix]
+ Prefix - Submit any string
+
+ The cli will now if you want to create another module entry.
+ Submit "N".
+ e. Default-operation
+ Submit "skip" to skip or pick from available e.g. merge, replace etc.
+ f. Error-option
+ Submit "skip" to skip option.
+ g. Config-target
+ This is a choice between running and candidate. Submit candidate to edit configuration only in the candidate datastore.
+ h. Test-option
+ Submit "skip" to skip option.
+ i. You should see OK response
+ j. You can check the candidate datastore change by executing get-config command as in scenario 4, but pick candidate as the source.
+ k. You should see this module in the output:
+ module [name=newModule] {
+ name newModule
+ type (urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?revision=2013-04-05)threadfactory-naming
+ choice configuration (threadfactory-naming) {
+ name-prefix prefix
+ }
+ }
+
+
+7. Commiting changes from candidate to running datastore
+ a. Execute scenario 6.
+ b. Type commit, hit TAB, hit Enter
+ c. Cli will ask for commit arguments: [confirm-timeout, confirmed, persist, persist-id]. We will skip all these arguments since they are not supported in ODL. Cli should be able to detect this and not ask for them. This is a TODO, by supporting feature/if-feature detection in the CLI.
+ d. Confirm-timeout
+ Skip
+ e. Confirmed
+ N
+ f. Persist
+ Skip
+ g. Persist-id
+ Skip
+ h. You should see OK response
+ i. You can check the candidate datastore change by executing get-config command as in scenario 4.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ </parent>
+ <artifactId>netconf-cli</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+
+ <!--
+ <properties>
+ <checkstyle.skip>true</checkstyle.skip>
+ </properties> -->
+
+ <dependencies>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <!--TODO configure properly -->
+ </dependency>
+ <dependency>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ <version>2.11</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sourceforge.argparse4j</groupId>
+ <artifactId>argparse4j</artifactId>
+ <version>0.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-netconf-connector</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ <!-- <version>0.6.2-SNAPSHOT</version>-->
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-json</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.opendaylight.controller.netconf.cli.Main</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <configuration></configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <mainClass>org.opendaylight.controller.netconf.cli.Main</mainClass>
+ </transformer>
+ </transformers>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>executable</shadedClassifierName>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /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.netconf.cli;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import jline.console.UserInterruptException;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import org.opendaylight.controller.netconf.cli.commands.Command;
+import org.opendaylight.controller.netconf.cli.commands.CommandConstants;
+import org.opendaylight.controller.netconf.cli.commands.CommandDispatcher;
+import org.opendaylight.controller.netconf.cli.commands.CommandInvocationException;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.controller.netconf.cli.writer.WriteException;
+import org.opendaylight.controller.netconf.cli.writer.Writer;
+import org.opendaylight.controller.netconf.cli.writer.impl.CompositeNodeWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+/**
+ * The top level cli state that dispatches command executions
+ */
+public class Cli implements Runnable {
+ private final CommandDispatcher commandRegistry;
+ private final CommandArgHandlerRegistry argumentHandlerRegistry;
+ private final SchemaContextRegistry schemaContextRegistry;
+ private final ConsoleIO consoleIO;
+
+ public Cli(final ConsoleIO consoleIO, final CommandDispatcher commandRegistry,
+ final CommandArgHandlerRegistry argumentHandlerRegistry, final SchemaContextRegistry schemaContextRegistry) {
+ this.consoleIO = consoleIO;
+ this.commandRegistry = commandRegistry;
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ this.schemaContextRegistry = schemaContextRegistry;
+ }
+
+ @Override
+ public void run() {
+ try {
+ consoleIO.writeLn("Cli is up, available commands:");
+ final RootConsoleContext consoleContext = new RootConsoleContext(commandRegistry);
+ consoleIO.enterContext(consoleContext);
+ consoleIO.complete();
+ consoleIO.writeLn("");
+
+ while (true) {
+ final String commandName = consoleIO.read();
+ final Optional<Command> commandOpt = commandRegistry.getCommand(commandName);
+
+ if (commandOpt.isPresent() == false) {
+ continue;
+ }
+
+ final Command command = commandOpt.get();
+ try {
+ consoleIO.enterContext(command.getConsoleContext());
+ final Output response = command.invoke(handleInput(command.getInputDefinition()));
+ handleOutput(command, response);
+ } catch (final CommandInvocationException e) {
+ consoleIO.write(e.getMessage());
+ } catch (final UserInterruptException e) {
+ consoleIO.writeLn("Command " + command.getCommandId() + " was terminated.");
+ } finally {
+ consoleIO.leaveContext();
+ }
+
+ }
+ } catch (final IOException e) {
+ throw new RuntimeException("IO failure", e);
+ }
+ }
+
+ private void handleOutput(final Command command, final Output response) {
+ final OutputDefinition outputDefinition = command.getOutputDefinition();
+
+ final Writer<DataSchemaNode> outHandler = argumentHandlerRegistry.getGenericWriter();
+ if (outputDefinition.isEmpty()) {
+ handleEmptyOutput(command, response);
+ } else {
+ handleRegularOutput(response, outputDefinition, outHandler);
+ }
+ }
+
+ private void handleRegularOutput(final Output response, final OutputDefinition outputDefinition,
+ final Writer<DataSchemaNode> outHandler) {
+ final Map<DataSchemaNode, List<Node<?>>> unwrap = response.unwrap(outputDefinition);
+
+ for (final DataSchemaNode schemaNode : unwrap.keySet()) {
+ Preconditions.checkNotNull(schemaNode);
+
+ try {
+
+ // FIXME move custom writer to GenericWriter/Serializers ...
+ // this checks only first level
+ final Optional<Class<? extends Writer<DataSchemaNode>>> customReaderClassOpt = tryGetCustomHandler(schemaNode);
+
+ if (customReaderClassOpt.isPresent()) {
+ final Writer<DataSchemaNode> customReaderInstance = argumentHandlerRegistry
+ .getCustomWriter(customReaderClassOpt.get());
+ Preconditions.checkNotNull(customReaderInstance, "Unknown custom writer: %s",
+ customReaderClassOpt.get());
+ customReaderInstance.write(schemaNode, unwrap.get(schemaNode));
+ } else {
+ outHandler.write(schemaNode, unwrap.get(schemaNode));
+ }
+
+ } catch (final WriteException e) {
+ throw new IllegalStateException("Unable to write value for: " + schemaNode.getQName() + " from: "
+ + unwrap.get(schemaNode), e);
+ }
+ }
+ }
+
+ private void handleEmptyOutput(final Command command, final Output response) {
+ try {
+ new CompositeNodeWriter(consoleIO, new OutFormatter()).write(null,
+ Collections.<Node<?>> singletonList(response.getOutput()));
+ } catch (final WriteException e) {
+ throw new IllegalStateException("Unable to write value for: " + response.getOutput().getNodeType()
+ + " from: " + command.getCommandId(), e);
+ }
+ }
+
+ private Input handleInput(final InputDefinition inputDefinition) {
+ List<Node<?>> allArgs = Collections.emptyList();
+ try {
+ if (!inputDefinition.isEmpty()) {
+ allArgs = argumentHandlerRegistry.getGenericReader(schemaContextRegistry.getLocalSchemaContext()).read(
+ inputDefinition.getInput());
+ }
+ } catch (final ReadingException e) {
+ throw new IllegalStateException("Unable to read value for: " + inputDefinition.getInput().getQName(), e);
+ }
+
+ return new Input(allArgs);
+ }
+
+ // TODO move tryGet to GenericWriter, GenericReader has the same code
+ private <T> Optional<Class<? extends T>> tryGetCustomHandler(final DataSchemaNode dataSchemaNode) {
+
+ for (final UnknownSchemaNode unknownSchemaNode : dataSchemaNode.getUnknownSchemaNodes()) {
+
+ if (isExtenstionForCustomHandler(unknownSchemaNode)) {
+ final String argumentHandlerClassName = unknownSchemaNode.getNodeParameter();
+ try {
+ final Class<?> argumentClass = Class.forName(argumentHandlerClassName);
+ // TODO add check before cast
+ return Optional.<Class<? extends T>> of((Class<? extends T>) argumentClass);
+ } catch (final ClassNotFoundException e) {
+ throw new IllegalArgumentException("Unknown custom reader class " + argumentHandlerClassName
+ + " for: " + dataSchemaNode.getQName());
+ }
+ }
+ }
+
+ return Optional.absent();
+ }
+
+ private boolean isExtenstionForCustomHandler(final UnknownSchemaNode unknownSchemaNode) {
+ final QName qName = unknownSchemaNode.getExtensionDefinition().getQName();
+ return qName.equals(CommandConstants.ARG_HANDLER_EXT_QNAME);
+ }
+
+ private static final class RootConsoleContext implements ConsoleContext {
+
+ private final Completer completer;
+
+ public RootConsoleContext(final CommandDispatcher commandRegistry) {
+ completer = new CommandCompleter(commandRegistry);
+ }
+
+ @Override
+ public Completer getCompleter() {
+ return completer;
+ }
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.absent();
+ }
+
+ private class CommandCompleter extends StringsCompleter {
+
+ private final CommandDispatcher commandRegistry;
+
+ public CommandCompleter(final CommandDispatcher commandRegistry) {
+ this.commandRegistry = commandRegistry;
+ }
+
+ @Override
+ public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
+ getStrings().clear();
+ getStrings().addAll(commandRegistry.getCommandIds());
+ return super.complete(buffer, cursor, candidates);
+ }
+ }
+ }
+
+}
--- /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.netconf.cli;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.Reader;
+import org.opendaylight.controller.netconf.cli.reader.custom.ConfigReader;
+import org.opendaylight.controller.netconf.cli.reader.custom.EditContentReader;
+import org.opendaylight.controller.netconf.cli.reader.custom.FilterReader;
+import org.opendaylight.controller.netconf.cli.reader.custom.PasswordReader;
+import org.opendaylight.controller.netconf.cli.reader.impl.GenericReader;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.controller.netconf.cli.writer.Writer;
+import org.opendaylight.controller.netconf.cli.writer.custom.DataWriter;
+import org.opendaylight.controller.netconf.cli.writer.impl.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Keeps custom and generic input/output arguments handlers. Custom handlers are
+ * constructed lazily, due to remote schema context acquisition.
+ */
+public class CommandArgHandlerRegistry {
+
+ private final ConsoleIO consoleIO;
+ private final SchemaContextRegistry schemaContextRegistry;
+
+ private final Map<Class<? extends Reader<? extends DataSchemaNode>>, ReaderProvider> customReaders = Maps
+ .newHashMap();
+ private final Map<Class<? extends Writer<DataSchemaNode>>, WriterProvider> customWriters = Maps.newHashMap();
+
+ public CommandArgHandlerRegistry(final ConsoleIO consoleIO, final SchemaContextRegistry schemaContextRegistry) {
+ this.consoleIO = consoleIO;
+ this.schemaContextRegistry = schemaContextRegistry;
+
+ setUpReaders();
+ setUpWriters();
+ }
+
+ private void setUpWriters() {
+ customWriters.put(DataWriter.class, new DataWriterProvider());
+ }
+
+ private void setUpReaders() {
+ customReaders.put(PasswordReader.class, new PasswordReaderProvider());
+ customReaders.put(FilterReader.class, new FilterReaderProvider());
+ customReaders.put(ConfigReader.class, new ConfigReaderProvider());
+ customReaders.put(EditContentReader.class, new EditContentReaderProvider());
+ }
+
+ public synchronized Reader<? extends DataSchemaNode> getCustomReader(
+ final Class<? extends Reader<DataSchemaNode>> readerType) {
+ return customReaders.get(readerType).provide(consoleIO, this, schemaContextRegistry);
+ }
+
+ private static SchemaContext getRemoteSchema(final Class<?> handlerType,
+ final SchemaContextRegistry schemaContextRegistry) {
+ final Optional<SchemaContext> remoteSchemaContext = schemaContextRegistry.getRemoteSchemaContext();
+ Preconditions.checkState(remoteSchemaContext.isPresent(),
+ "Remote schema context not acquired yet, cannot get handler %s", handlerType);
+ return remoteSchemaContext.get();
+ }
+
+ public synchronized Reader<DataSchemaNode> getGenericReader(final SchemaContext schemaContext) {
+ return new GenericReader(consoleIO, this, schemaContext);
+ }
+
+ public synchronized Reader<DataSchemaNode> getGenericReader(final SchemaContext schemaContext,
+ final boolean readConfigNode) {
+ return new GenericReader(consoleIO, this, schemaContext, readConfigNode);
+ }
+
+ public synchronized Writer<DataSchemaNode> getCustomWriter(final Class<? extends Writer<DataSchemaNode>> writerType) {
+ return customWriters.get(writerType).provide(consoleIO, getRemoteSchema(writerType, schemaContextRegistry),
+ this);
+ }
+
+ public synchronized Writer<DataSchemaNode> getGenericWriter() {
+ return new NormalizedNodeWriter(consoleIO, new OutFormatter());
+ }
+
+ /**
+ * Reader providers, in order to construct readers lazily
+ */
+ private static interface ReaderProvider {
+ Reader<? extends DataSchemaNode> provide(ConsoleIO consoleIO,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry,
+ final SchemaContextRegistry schemaContextRegistry);
+ }
+
+ private static final class FilterReaderProvider implements ReaderProvider {
+ @Override
+ public Reader<? extends DataSchemaNode> provide(final ConsoleIO consoleIO,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry,
+ final SchemaContextRegistry schemaContextRegistry) {
+ return new FilterReader(consoleIO, getRemoteSchema(FilterReader.class, schemaContextRegistry));
+ }
+ }
+
+ private static final class ConfigReaderProvider implements ReaderProvider {
+ @Override
+ public Reader<? extends DataSchemaNode> provide(final ConsoleIO consoleIO,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry,
+ final SchemaContextRegistry schemaContextRegistry) {
+ return new ConfigReader(consoleIO, getRemoteSchema(ConfigReader.class, schemaContextRegistry),
+ commandArgHandlerRegistry);
+ }
+ }
+
+ private static final class EditContentReaderProvider implements ReaderProvider {
+ @Override
+ public Reader<? extends DataSchemaNode> provide(final ConsoleIO consoleIO,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry,
+ final SchemaContextRegistry schemaContextRegistry) {
+ return new EditContentReader(consoleIO, commandArgHandlerRegistry, getRemoteSchema(EditContentReader.class,
+ schemaContextRegistry));
+ }
+ }
+
+ private static final class PasswordReaderProvider implements ReaderProvider {
+ @Override
+ public Reader<? extends DataSchemaNode> provide(final ConsoleIO consoleIO,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry,
+ final SchemaContextRegistry schemaContextRegistry) {
+ return new PasswordReader(consoleIO, schemaContextRegistry.getLocalSchemaContext());
+ }
+ }
+
+ /**
+ * Writer providers, in order to construct readers lazily
+ */
+ private static interface WriterProvider {
+ Writer<DataSchemaNode> provide(ConsoleIO consoleIO, SchemaContext schema,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry);
+ }
+
+ private class DataWriterProvider implements WriterProvider {
+ @Override
+ public Writer<DataSchemaNode> provide(final ConsoleIO consoleIO, final SchemaContext schema,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry) {
+ return new DataWriter(consoleIO, new OutFormatter(), schema);
+ }
+ }
+}
--- /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.netconf.cli;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.inf.ArgumentGroup;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+import net.sourceforge.argparse4j.inf.Namespace;
+import org.opendaylight.controller.netconf.cli.commands.CommandDispatcher;
+import org.opendaylight.controller.netconf.cli.commands.local.Connect;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIOImpl;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Parse arguments, start remote device connection and start CLI after the
+ * connection is fully up
+ */
+public class Main {
+
+ public static void main(final String[] args) {
+ final CliArgumentParser cliArgs = new CliArgumentParser();
+ try {
+ cliArgs.parse(args);
+ } catch (final ArgumentParserException e) {
+ // Just end the cli, exception was handled by the CliArgumentParser
+ return;
+ }
+
+ final ConsoleIO consoleIO;
+ try {
+ consoleIO = new ConsoleIOImpl();
+ } catch (final IOException e) {
+ handleStartupException(e);
+ return;
+ }
+
+ final SchemaContext localSchema = CommandDispatcher.parseSchema(CommandDispatcher.LOCAL_SCHEMA_PATHS);
+ final SchemaContextRegistry schemaContextRegistry = new SchemaContextRegistry(localSchema);
+
+ final CommandDispatcher commandDispatcher = new CommandDispatcher();
+ final CommandArgHandlerRegistry argumentHandlerRegistry = new CommandArgHandlerRegistry(consoleIO,
+ schemaContextRegistry);
+ final NetconfDeviceConnectionManager connectionManager = new NetconfDeviceConnectionManager(commandDispatcher,
+ argumentHandlerRegistry, schemaContextRegistry, consoleIO);
+
+ commandDispatcher.addLocalCommands(connectionManager, localSchema, cliArgs.getConnectionTimeoutMs());
+
+ switch (cliArgs.connectionArgsPresent()) {
+ case TCP: {
+ // FIXME support pure TCP
+ handleRunningException(new UnsupportedOperationException("PURE TCP CONNECTIONS ARE NOT SUPPORTED YET, USE SSH INSTEAD BY PROVIDING USERNAME AND PASSWORD AS WELL"));
+ return;
+ }
+ case SSH: {
+ writeStatus(consoleIO, "Connecting to %s via SSH. Please wait.", cliArgs.getAddress());
+ connectionManager.connectBlocking(cliArgs.getAddress(), getClientSshConfig(cliArgs));
+ break;
+ }
+ case NONE: {/* Do not connect initially */
+ writeStatus(consoleIO, "No initial connection. To connect use the connect command");
+ }
+ }
+
+ try {
+ new Cli(consoleIO, commandDispatcher, argumentHandlerRegistry, schemaContextRegistry).run();
+ } catch (final Exception e) {
+ // TODO Running exceptions have to be handled properly
+ handleRunningException(e);
+ System.exit(0);
+ }
+ }
+
+ private static NetconfClientConfigurationBuilder getClientConfig(final CliArgumentParser cliArgs) {
+ return NetconfClientConfigurationBuilder.create().withAddress(cliArgs.getServerAddress())
+ .withConnectionTimeoutMillis(cliArgs.getConnectionTimeoutMs())
+ .withReconnectStrategy(Connect.getReconnectStrategy())
+ .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP);
+ }
+
+ private static NetconfClientConfigurationBuilder getClientSshConfig(final CliArgumentParser cliArgs) {
+ return NetconfClientConfigurationBuilder.create().withAddress(cliArgs.getServerAddress())
+ .withConnectionTimeoutMillis(cliArgs.getConnectionTimeoutMs())
+ .withReconnectStrategy(Connect.getReconnectStrategy())
+ .withAuthHandler(cliArgs.getCredentials())
+ .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH);
+ }
+
+ private static void handleStartupException(final IOException e) {
+ handleException(e, "Unable to initialize CLI");
+ }
+
+ private static void handleException(final Exception e, final String message) {
+ System.err.println(message);
+ e.printStackTrace(System.err);
+ }
+
+ private static void writeStatus(final ConsoleIO io, final String blueprint, final Object... args) {
+ try {
+ io.formatLn(blueprint, args);
+ } catch (final IOException e) {
+ handleStartupException(e);
+ }
+ }
+
+ private static void handleRunningException(final Exception e) {
+ handleException(e, "Unexpected CLI runtime exception");
+ }
+
+ private static final class CliArgumentParser {
+
+ public static final String USERNAME = "username";
+ public static final String PASSWORD = "password";
+ public static final String SERVER = "server";
+ public static final String PORT = "port";
+
+ public static final String CONNECT_TIMEOUT = "connectionTimeout";
+ public static final int DEFAULT_CONNECTION_TIMEOUT_MS = 50000;
+
+ private final ArgumentParser parser;
+ private Namespace parsed;
+
+ private CliArgumentParser() {
+ parser = ArgumentParsers.newArgumentParser("Netconf cli").defaultHelp(true)
+ .description("Generic cli for netconf devices")
+ .usage("Submit address + port for initial TCP connection (PURE TCP CONNECTIONS ARE NOT SUPPORTED YET)\n" +
+ "Submit username + password in addition to address + port for initial SSH connection\n" +
+ "If no arguments(or unexpected combination) is submitted, cli will be started without initial connection\n" +
+ "To use with ODL controller, run with: java -jar netconf-cli-0.2.5-SNAPSHOT-executable.jar --server localhost --port 1830 --username admin --password admin");
+
+ final ArgumentGroup tcpGroup = parser.addArgumentGroup("TCP")
+ .description("Base arguments to initiate TCP connection right away");
+
+ tcpGroup.addArgument("--" + SERVER).help("Netconf device ip-address/domain name");
+ tcpGroup.addArgument("--" + PORT).type(Integer.class).help("Netconf device port");
+ tcpGroup.addArgument("--" + CONNECT_TIMEOUT)
+ .type(Integer.class)
+ .setDefault(DEFAULT_CONNECTION_TIMEOUT_MS)
+ .help("Timeout(in ms) for connection to succeed, if the connection is not fully established by the time is up, " +
+ "connection attempt is considered a failure. This attribute is not working as expected yet");
+
+ final ArgumentGroup sshGroup = parser.addArgumentGroup("SSH")
+ .description("SSH credentials, if provided, initial connection will be attempted using SSH");
+
+ sshGroup.addArgument("--" + USERNAME).help("Username for SSH connection");
+ sshGroup.addArgument("--" + PASSWORD).help("Password for SSH connection");
+ }
+
+ public void parse(final String[] args) throws ArgumentParserException {
+ try {
+ this.parsed = parser.parseArgs(args);
+ } catch (final ArgumentParserException e) {
+ parser.handleError(e);
+ throw e;
+ }
+ }
+
+ public InetSocketAddress getServerAddress() {
+ try {
+ return new InetSocketAddress(InetAddress.getByName(getAddress()), getPort());
+ } catch (final UnknownHostException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private Integer getPort() {
+ checkParsed();
+ return parsed.getInt(PORT);
+ }
+
+ private String getAddress() {
+ checkParsed();
+ return getString(SERVER);
+ }
+
+ private Integer getConnectionTimeoutMs() {
+ checkParsed();
+ return parsed.getInt(CONNECT_TIMEOUT);
+ }
+
+ private void checkParsed() {
+ Preconditions.checkState(parsed != null, "No arguments were parsed yet");
+ }
+
+ public String getUsername() {
+ checkParsed();
+ return getString(USERNAME);
+ }
+
+ private String getString(final String key) {
+ return parsed.getString(key);
+ }
+
+ public LoginPassword getCredentials() {
+ return new LoginPassword(getUsername(), getPassword());
+ }
+
+ public String getPassword() {
+ checkParsed();
+ return getString(PASSWORD);
+ }
+
+ public InitialConnectionType connectionArgsPresent() {
+ if(getAddress() != null && getPort() != null) {
+ if(getUsername() != null && getPassword() != null) {
+ return InitialConnectionType.SSH;
+ }
+ return InitialConnectionType.TCP;
+ }
+ return InitialConnectionType.NONE;
+ }
+
+ enum InitialConnectionType {
+ TCP, SSH, NONE
+ }
+ }
+}
--- /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.netconf.cli;
+
+import com.google.common.base.Optional;
+import jline.console.completer.Completer;
+import jline.console.completer.NullCompleter;
+import org.opendaylight.controller.netconf.cli.commands.CommandDispatcher;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+/**
+ * Implementation of RemoteDeviceHandler. Integrates cli with
+ * sal-netconf-connector.
+ */
+public class NetconfDeviceConnectionHandler implements RemoteDeviceHandler<NetconfSessionCapabilities> {
+
+ private final CommandDispatcher commandDispatcher;
+ private final SchemaContextRegistry schemaContextRegistry;
+ private final ConsoleIO console;
+ private final String deviceId;
+
+ private boolean up = false;
+
+ public NetconfDeviceConnectionHandler(final CommandDispatcher commandDispatcher,
+ final SchemaContextRegistry schemaContextRegistry, final ConsoleIO console, final String deviceId) {
+ this.commandDispatcher = commandDispatcher;
+ this.schemaContextRegistry = schemaContextRegistry;
+ this.console = console;
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public synchronized void onDeviceConnected(final SchemaContextProvider contextProvider,
+ final NetconfSessionCapabilities capabilities, final RpcImplementation rpcImplementation) {
+ console.enterRootContext(new ConsoleContext() {
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.of(deviceId);
+ }
+
+ @Override
+ public Completer getCompleter() {
+ return new NullCompleter();
+ }
+ });
+
+ // TODO Load schemas for base netconf + inet types from remote device if
+ // possible
+ // TODO detect netconf base version
+ // TODO detect inet types version
+ commandDispatcher.addRemoteCommands(rpcImplementation, contextProvider.getSchemaContext());
+ schemaContextRegistry.setRemoteSchemaContext(contextProvider.getSchemaContext());
+ up = true;
+ this.notify();
+ }
+
+ /**
+ * @return true if connection was fully established
+ */
+ public synchronized boolean isUp() {
+ return up;
+ }
+
+ @Override
+ public synchronized void onDeviceDisconnected() {
+ console.leaveRootContext();
+ commandDispatcher.removeRemoteCommands();
+ schemaContextRegistry.setRemoteSchemaContext(null);
+ up = false;
+ }
+
+ @Override
+ public void onNotification(final CompositeNode compositeNode) {
+ // FIXME
+ }
+
+ @Override
+ public void close() {
+ // FIXME
+ }
+}
--- /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.netconf.cli;
+
+import com.google.common.base.Preconditions;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.opendaylight.controller.netconf.cli.commands.CommandDispatcher;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders;
+
+/**
+ * Manages connect/disconnect to 1 remote device
+ */
+public class NetconfDeviceConnectionManager implements Closeable {
+
+ private final CommandDispatcher commandDispatcher;
+ private final SchemaContextRegistry schemaContextRegistry;
+ private final ConsoleIO console;
+
+ private final ExecutorService executor;
+ private final NioEventLoopGroup nettyThreadGroup;
+ private final NetconfClientDispatcherImpl netconfClientDispatcher;
+
+ // Connection
+ private NetconfDeviceConnectionHandler handler;
+ private NetconfDevice device;
+ private NetconfDeviceCommunicator listener;
+
+ public NetconfDeviceConnectionManager(final CommandDispatcher commandDispatcher,
+ final CommandArgHandlerRegistry argumentHandlerRegistry, final SchemaContextRegistry schemaContextRegistry,
+ final ConsoleIO consoleIO) {
+ this.commandDispatcher = commandDispatcher;
+ this.schemaContextRegistry = schemaContextRegistry;
+ this.console = consoleIO;
+
+ executor = Executors.newSingleThreadExecutor();
+ nettyThreadGroup = new NioEventLoopGroup();
+ netconfClientDispatcher = new NetconfClientDispatcherImpl(nettyThreadGroup, nettyThreadGroup,
+ new HashedWheelTimer());
+ }
+
+ // TODO we receive configBuilder in order to add SessionListener, Session
+ // Listener should not be part of config
+ public synchronized void connect(final String name, final NetconfClientConfigurationBuilder configBuilder) {
+ // TODO change IllegalState exceptions to custom ConnectionException
+ Preconditions.checkState(listener == null, "Already connected");
+
+ final RemoteDeviceId deviceId = new RemoteDeviceId(name);
+
+ handler = new NetconfDeviceConnectionHandler(commandDispatcher, schemaContextRegistry,
+ console, name);
+ device = NetconfDevice.createNetconfDevice(deviceId, getGlobalNetconfSchemaProvider(), executor, handler);
+ listener = new NetconfDeviceCommunicator(deviceId, device);
+ configBuilder.withSessionListener(listener);
+ listener.initializeRemoteConnection(netconfClientDispatcher, configBuilder.build());
+ }
+
+ /**
+ * Blocks thread until connection is fully established
+ */
+ public synchronized Set<String> connectBlocking(final String name, final NetconfClientConfigurationBuilder configBuilder) {
+ this.connect(name, configBuilder);
+ synchronized (handler) {
+ while (handler.isUp() == false) {
+ try {
+ // TODO implement Timeout for unsuccessful connection
+ handler.wait();
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalArgumentException(e);
+ }
+ }
+ }
+
+ return commandDispatcher.getRemoteCommandIds();
+ }
+
+ public synchronized void disconnect() {
+ Preconditions.checkState(listener != null, "Not connected yet");
+ Preconditions.checkState(handler.isUp(), "Not connected yet");
+ listener.close();
+ listener = null;
+ device = null;
+ handler.close();
+ handler = null;
+ }
+
+ private static AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider() {
+ // FIXME move to args
+ final String storageFile = "cache/schema";
+ final File directory = new File(storageFile);
+ final SchemaSourceProvider<String> defaultProvider = SchemaSourceProviders.noopProvider();
+ return FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory);
+ }
+
+ @Override
+ public void close() throws IOException {
+ executor.shutdownNow();
+ nettyThreadGroup.shutdownGracefully();
+ }
+}
--- /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.netconf.cli;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Contains the local schema context (containing local commands) and remote schema context (remote commands)
+ *
+ * Remote commands are set only after the connection is fully established. So classes using the remote schema context
+ */
+public class SchemaContextRegistry {
+
+ private final SchemaContext localSchemaContext;
+ private SchemaContext remoteSchemaContext;
+
+ public SchemaContextRegistry(final SchemaContext localSchemaContext) {
+ this.localSchemaContext = localSchemaContext;
+ }
+
+ public synchronized Optional<SchemaContext> getRemoteSchemaContext() {
+ return Optional.fromNullable(remoteSchemaContext);
+ }
+
+ public SchemaContext getLocalSchemaContext() {
+ return localSchemaContext;
+ }
+
+ public synchronized void setRemoteSchemaContext(final SchemaContext remoteSchemaContext) {
+ this.remoteSchemaContext = remoteSchemaContext;
+ }
+}
--- /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.netconf.cli.commands;
+
+import com.google.common.base.Optional;
+import jline.console.completer.Completer;
+import jline.console.completer.NullCompleter;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+public abstract class AbstractCommand implements Command {
+
+ private final QName qName;
+ private final InputDefinition args;
+ private final OutputDefinition output;
+ private final String description;
+
+ public AbstractCommand(final QName qName, final InputDefinition args, final OutputDefinition output,
+ final String description) {
+ this.qName = qName;
+ this.args = args;
+ this.output = output;
+ this.description = description;
+ }
+
+ protected static OutputDefinition getOutputDefinition(final RpcDefinition rpcDefinition) {
+ final ContainerSchemaNode output = rpcDefinition.getOutput();
+ return output != null ? OutputDefinition.fromOutput(output) : OutputDefinition.empty();
+ }
+
+ protected static InputDefinition getInputDefinition(final RpcDefinition rpcDefinition) {
+ final ContainerSchemaNode input = rpcDefinition.getInput();
+ return InputDefinition.fromInput(input);
+ }
+
+ @Override
+ public InputDefinition getInputDefinition() {
+ return args;
+ }
+
+ @Override
+ public OutputDefinition getOutputDefinition() {
+ return output;
+ }
+
+ @Override
+ public QName getCommandId() {
+ return qName;
+ }
+
+ @Override
+ public ConsoleContext getConsoleContext() {
+ return new ConsoleContext() {
+
+ @Override
+ public Completer getCompleter() {
+ return new NullCompleter();
+ }
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.of(qName.getLocalName());
+ }
+ };
+ }
+
+ @Override
+ public Optional<String> getCommandDescription() {
+ return Optional.fromNullable(description);
+ }
+}
--- /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.netconf.cli.commands;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Local command e.g. help or remote rpc e.g. get-config must conform to this interface
+ */
+public interface Command {
+
+ Output invoke(Input inputArgs) throws CommandInvocationException;
+
+ InputDefinition getInputDefinition();
+
+ OutputDefinition getOutputDefinition();
+
+ QName getCommandId();
+
+ Optional<String> getCommandDescription();
+
+ ConsoleContext getConsoleContext();
+}
--- /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.netconf.cli.commands;
+
+import java.net.URI;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class CommandConstants {
+
+ // Local command ids are defined here, this links the implementation to the rpc definition in yang
+ // Better way needs to be found to provide this link instead of hardcoded QNames (e.g. yang extension)
+ public static final QName HELP_QNAME = QName.create(URI.create("netconf:cli"), IOUtil.parseDate("2014-05-22"), "help");
+ public static final QName CLOSE_QNAME = QName.create(HELP_QNAME, "close");
+ public static final QName CONNECT_QNAME = QName.create(HELP_QNAME, "connect");
+ public static final QName DISCONNECT_QNAME = QName.create(CONNECT_QNAME, "disconnect");
+
+ public static final QName ARG_HANDLER_EXT_QNAME = QName.create(
+ URI.create("urn:ietf:params:xml:ns:netconf:base:1.0:cli"), IOUtil.parseDate("2014-05-26"),
+ "argument-handler");
+
+ public static final QName NETCONF_BASE_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "2011-06-01",
+ "netconf");
+}
--- /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.netconf.cli.commands;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.netconf.cli.NetconfDeviceConnectionHandler;
+import org.opendaylight.controller.netconf.cli.NetconfDeviceConnectionManager;
+import org.opendaylight.controller.netconf.cli.commands.local.Close;
+import org.opendaylight.controller.netconf.cli.commands.local.Connect;
+import org.opendaylight.controller.netconf.cli.commands.local.Disconnect;
+import org.opendaylight.controller.netconf.cli.commands.local.Help;
+import org.opendaylight.controller.netconf.cli.commands.remote.RemoteCommand;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+/**
+ * The registry of available commands local + remote. Created from schema contexts.
+ */
+public class CommandDispatcher {
+
+ // TODO extract interface
+
+ private final Map<QName, Command> localCommands = Maps.newHashMap();
+ private final Map<String, QName> nameToQNameLocal = Maps.newHashMap();
+
+ private final Map<QName, Command> remoteCommands = Maps.newHashMap();
+ private final Map<String, QName> nameToQNameRemote = Maps.newHashMap();
+
+ public synchronized Map<QName, Command> getCommands() {
+ return Collections.unmodifiableMap(mergeCommands());
+ }
+
+ private Map<QName, Command> mergeCommands() {
+ // TODO cache this merged map
+ return mergeMaps(remoteCommands, localCommands);
+ }
+
+ private Map<String, QName> mergeCommandIds() {
+ // TODO cache this merged map
+ return mergeMaps(nameToQNameRemote, nameToQNameLocal);
+ }
+
+ private <K, V> Map<K, V> mergeMaps(final Map<K, V> remoteMap, final Map<K, V> localMap) {
+ final Map<K, V> mergedCommands = Maps.newHashMap();
+ mergedCommands.putAll(remoteMap);
+ mergedCommands.putAll(localMap);
+ return mergedCommands;
+ }
+
+ public synchronized Set<String> getCommandIds() {
+ return mergeCommandIds().keySet();
+ }
+
+ public synchronized Set<String> getRemoteCommandIds() {
+ return nameToQNameRemote.keySet();
+ }
+
+ public synchronized Optional<Command> getCommand(final String nameWithModule) {
+ final QName commandQName = mergeCommandIds().get(nameWithModule);
+ final Map<QName, Command> qNameCommandMap = mergeCommands();
+ if(commandQName == null || qNameCommandMap.containsKey(commandQName) == false) {
+ return Optional.absent();
+ }
+
+ return Optional.of(qNameCommandMap.get(commandQName));
+ }
+
+ public synchronized Optional<Command> getCommand(final QName qName) {
+ return Optional.fromNullable(mergeCommands().get(qName));
+ }
+
+ private static Optional<Command> getCommand(final Map<String, QName> commandNameMap, final Map<QName, Command> commands, final String nameWithModule) {
+ final QName qName = commandNameMap.get(nameWithModule);
+ if(qName == null)
+ return Optional.absent();
+
+ final Command command = commands.get(qName);
+ if(command == null) {
+ return Optional.absent();
+ }
+
+ return Optional.of(command);
+ }
+
+ public static final Collection<String> BASE_NETCONF_SCHEMA_PATHS = Lists.newArrayList("/schema/remote/ietf-netconf.yang",
+ "/schema/common/netconf-cli-ext.yang", "/schema/common/ietf-inet-types.yang");
+
+ public synchronized void addRemoteCommands(final RpcImplementation rpcInvoker, final SchemaContext remoteSchema) {
+ this.addRemoteCommands(rpcInvoker, remoteSchema, parseSchema(BASE_NETCONF_SCHEMA_PATHS));
+ }
+
+ public synchronized void addRemoteCommands(final RpcImplementation rpcInvoker, final SchemaContext remoteSchema, final SchemaContext baseNetconfSchema) {
+ for (final SchemaContext context : Lists.newArrayList(remoteSchema, baseNetconfSchema)) {
+ for (final Module module : context.getModules()) {
+ for (final RpcDefinition rpcDefinition : module.getRpcs()) {
+ final Command command = RemoteCommand.fromRpc(rpcDefinition, rpcInvoker);
+ remoteCommands.put(rpcDefinition.getQName(), command);
+ nameToQNameRemote.put(getCommandName(rpcDefinition, module), rpcDefinition.getQName());
+ }
+ }
+ }
+ }
+
+ public synchronized void removeRemoteCommands() {
+ remoteCommands.clear();
+ nameToQNameRemote.clear();
+ }
+
+ public static final Collection<String> LOCAL_SCHEMA_PATHS = Lists.newArrayList("/schema/local/netconf-cli.yang", "/schema/common/netconf-cli-ext.yang",
+ "/schema/common/ietf-inet-types.yang");
+
+ public synchronized void addLocalCommands(final NetconfDeviceConnectionManager connectionManager, final SchemaContext localSchema, final Integer connectionTimeout) {
+ for (final Module module : localSchema.getModules()) {
+ for (final RpcDefinition rpcDefinition : module.getRpcs()) {
+
+ // FIXME make local commands extensible
+ // e.g. by yang extension defining java class to be instantiated
+ // problem is with command specific resources
+ // e.g. Help would need command registry
+ final Command localCommand;
+ if (rpcDefinition.getQName().equals(CommandConstants.HELP_QNAME)) {
+ localCommand = Help.create(rpcDefinition, this);
+ } else if (rpcDefinition.getQName().equals(CommandConstants.CLOSE_QNAME)) {
+ localCommand = Close.create(rpcDefinition);
+ } else if (rpcDefinition.getQName().equals(CommandConstants.CONNECT_QNAME)) {
+ localCommand = Connect.create(rpcDefinition, connectionManager, connectionTimeout);
+ } else if (rpcDefinition.getQName().equals(CommandConstants.DISCONNECT_QNAME)) {
+ localCommand = Disconnect.create(rpcDefinition, connectionManager);
+ } else {
+ throw new IllegalStateException("No command implementation available for local command: " + rpcDefinition.getQName());
+ }
+
+ localCommands.put(localCommand.getCommandId(), localCommand);
+ nameToQNameLocal.put(getCommandName(rpcDefinition, module), localCommand.getCommandId());
+ }
+ }
+ }
+
+ private static String getCommandName(final RpcDefinition rpcDefinition, final Module module) {
+ return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
+ }
+
+ public static SchemaContext parseSchema(final Collection<String> yangPath) {
+ final YangParserImpl yangParserImpl = new YangParserImpl();
+ // TODO change deprecated method
+ final Set<Module> modules = yangParserImpl.parseYangModelsFromStreams(loadYangs(yangPath));
+ return yangParserImpl.resolveSchemaContext(modules);
+ }
+
+ private static List<InputStream> loadYangs(final Collection<String> yangPaths) {
+
+ return Lists.newArrayList(Collections2.transform(Lists.newArrayList(yangPaths),
+ new Function<String, InputStream>() {
+ @Override
+ public InputStream apply(final String input) {
+ final InputStream resourceAsStream = NetconfDeviceConnectionHandler.class.getResourceAsStream(input);
+ Preconditions.checkNotNull(resourceAsStream, "File %s was null", input);
+ return resourceAsStream;
+ }
+ }));
+ }
+}
--- /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.netconf.cli.commands;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class CommandInvocationException extends Exception {
+
+ public CommandInvocationException(final QName qName, final Throwable cause) {
+ this("Command " + qName + " invocation failed: " + cause.getMessage(), cause);
+ }
+
+ protected CommandInvocationException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public static class CommandTimeoutException extends CommandInvocationException {
+
+ public CommandTimeoutException(final QName qName, final Throwable e) {
+ super("Command " + qName + " timed out: " + e.getMessage(), e);
+ }
+ }
+}
--- /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.netconf.cli.commands.input;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+
+/**
+ * Input arguments for and rpc/command execution
+ */
+public class Input {
+
+ private final List<Node<?>> args;
+
+ private final Map<String, Node<?>> nameToArg = new HashMap<String, Node<?>>();
+
+ public Input(final List<Node<?>> args) {
+ // FIXME empty Input should be constructed from static factory method
+ if(args.isEmpty()) {
+ this.args = Collections.emptyList();
+ return;
+ }
+
+ final Node<?> input = args.iterator().next();
+ Preconditions
+ .checkArgument(input instanceof CompositeNode, "Input container has to be of type composite node.");
+ this.args = ((CompositeNode) input).getValue();
+
+ for (final Node<?> arg : this.args) {
+ nameToArg.put(arg.getNodeType().getLocalName(), arg);
+ }
+ }
+
+ public Node<?> getArg(final String name) {
+ return nameToArg.get(name);
+ }
+
+ public CompositeNode wrap(final QName rpcQName) {
+ return new CompositeNodeTOImpl(rpcQName, null, args);
+ }
+}
--- /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.netconf.cli.commands.input;
+
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * The definition of input arguments represented by schema nodes parsed from
+ * yang rpc definition
+ */
+public class InputDefinition {
+
+ private final ContainerSchemaNode inputContainer;
+
+ public InputDefinition(final ContainerSchemaNode inputContainer) {
+ this.inputContainer = inputContainer;
+ }
+
+ public static InputDefinition fromInput(final ContainerSchemaNode input) {
+ return new InputDefinition(input);
+ }
+
+ public ContainerSchemaNode getInput() {
+ return inputContainer;
+ }
+
+ // FIXME add empty as in output
+ public boolean isEmpty() {
+ return inputContainer == null;
+ }
+
+}
--- /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.netconf.cli.commands.local;
+
+import org.opendaylight.controller.netconf.cli.commands.AbstractCommand;
+import org.opendaylight.controller.netconf.cli.commands.Command;
+import org.opendaylight.controller.netconf.cli.commands.CommandInvocationException;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+/**
+ * Local command to shut down the cli
+ */
+public class Close extends AbstractCommand {
+
+ public Close(final QName qName, final InputDefinition args, final OutputDefinition output, final String description) {
+ super(qName, args, output, description);
+ }
+
+ @Override
+ public Output invoke(final Input inputArgs) throws CommandInvocationException {
+ // FIXME clean up, close session and then close
+ System.exit(0);
+ return null;
+ }
+
+ public static Command create(final RpcDefinition rpcDefinition) {
+ return new Close(rpcDefinition.getQName(), getInputDefinition(rpcDefinition),
+ getOutputDefinition(rpcDefinition), rpcDefinition.getDescription());
+ }
+
+}
--- /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.netconf.cli.commands.local;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Set;
+import org.opendaylight.controller.netconf.cli.NetconfDeviceConnectionManager;
+import org.opendaylight.controller.netconf.cli.commands.AbstractCommand;
+import org.opendaylight.controller.netconf.cli.commands.Command;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
+import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.yangtools.yang.common.QName;
+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.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+/**
+ * Local command to connect to a remote device
+ */
+public class Connect extends AbstractCommand {
+
+ private final NetconfDeviceConnectionManager connectManager;
+ private final Integer connectionTimeout;
+
+ private Connect(final QName qName, final InputDefinition args, final OutputDefinition output,
+ final NetconfDeviceConnectionManager connectManager, final String description, final Integer connectionTimeout) {
+ super(qName, args, output, description);
+ this.connectManager = connectManager;
+ this.connectionTimeout = connectionTimeout;
+ }
+
+ @Override
+ public Output invoke(final Input inputArgs) {
+ final NetconfClientConfigurationBuilder config = getConfig(inputArgs);
+ return invoke(config, getArgument(inputArgs, "address-name", String.class));
+ }
+
+ private Output invoke(final NetconfClientConfigurationBuilder config, final String addressName) {
+ final Set<String> remoteCmds = connectManager.connectBlocking(addressName, config);
+
+ final ArrayList<Node<?>> output = Lists.newArrayList();
+ output.add(new SimpleNodeTOImpl<>(QName.create(getCommandId(), "status"), null, "Connection initiated"));
+
+ for (final String cmdId : remoteCmds) {
+ output.add(new SimpleNodeTOImpl<>(QName.create(getCommandId(), "remote-commands"), null, cmdId));
+ }
+
+ return new Output(new CompositeNodeTOImpl(getCommandId(), null, output));
+ }
+
+ private NetconfClientConfigurationBuilder getConfig(final Input inputArgs) {
+
+ final ReconnectStrategy strategy = getReconnectStrategy();
+
+ final String address = getArgument(inputArgs, "address-name", String.class);
+ final Integer port = getArgument(inputArgs, "address-port", Integer.class);
+ final String username = getArgument(inputArgs, "user-name", String.class);
+ final String passwd = getArgument(inputArgs, "user-password", String.class);
+
+ final InetSocketAddress inetAddress;
+ try {
+ inetAddress = new InetSocketAddress(InetAddress.getByName(address), port);
+ } catch (final UnknownHostException e) {
+ throw new IllegalArgumentException("Unable to use address: " + address, e);
+ }
+
+ return NetconfClientConfigurationBuilder.create().withAddress(inetAddress)
+ .withConnectionTimeoutMillis(connectionTimeout)
+ .withReconnectStrategy(strategy)
+ .withAuthHandler(new LoginPassword(username, passwd))
+ .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH);
+ }
+
+ private <T> Optional<T> getArgumentOpt(final Input inputArgs, final String argName, final Class<T> type) {
+ final QName argQName = QName.create(getCommandId(), argName);
+ final Node<?> argumentNode = inputArgs.getArg(argName);
+ if (argumentNode == null) {
+ return Optional.absent();
+ }
+ Preconditions.checkArgument(argumentNode instanceof SimpleNode, "Only simple type argument supported, %s",
+ argQName);
+
+ final Object value = argumentNode.getValue();
+ Preconditions.checkArgument(type.isInstance(value), "Unexpected instance type: %s for argument: %s",
+ value.getClass(), argQName);
+ return Optional.of(type.cast(value));
+ }
+
+ private <T> T getArgument(final Input inputArgs, final String argName, final Class<T> type) {
+ final Optional<T> argumentOpt = getArgumentOpt(inputArgs, argName, type);
+ Preconditions.checkState(argumentOpt.isPresent(), "Argument: %s is missing but is required", argName);
+ return argumentOpt.get();
+ }
+
+ public static ReconnectStrategy getReconnectStrategy() {
+ // FIXME move to args either start-up args or input nodes for connect or both
+ return new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 1000);
+ }
+
+ public static Command create(final RpcDefinition rpcDefinition, final NetconfDeviceConnectionManager connectManager, final Integer connectionTimeout) {
+ return new Connect(rpcDefinition.getQName(), getInputDefinition(rpcDefinition),
+ getOutputDefinition(rpcDefinition), connectManager, rpcDefinition.getDescription(), connectionTimeout);
+ }
+}
--- /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.netconf.cli.commands.local;
+
+import com.google.common.collect.Lists;
+import org.opendaylight.controller.netconf.cli.NetconfDeviceConnectionManager;
+import org.opendaylight.controller.netconf.cli.commands.AbstractCommand;
+import org.opendaylight.controller.netconf.cli.commands.Command;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+/**
+ * Local disconnect command
+ */
+public class Disconnect extends AbstractCommand {
+
+ private final NetconfDeviceConnectionManager connectionManager;
+
+ public Disconnect(final QName qName, final InputDefinition inputDefinition,
+ final OutputDefinition outputDefinition, final NetconfDeviceConnectionManager connectionManager,
+ final String description) {
+ super(qName, inputDefinition, outputDefinition, description);
+ this.connectionManager = connectionManager;
+ }
+
+ @Override
+ public Output invoke(final Input inputArgs) {
+ connectionManager.disconnect();
+
+ return new Output(new CompositeNodeTOImpl(getCommandId(), null,
+ Lists.<Node<?>> newArrayList(new SimpleNodeTOImpl<>(new QName(getCommandId(), "status"), null,
+ "Connection disconnected"))));
+ }
+
+ public static Command create(final RpcDefinition rpcDefinition,
+ final NetconfDeviceConnectionManager commandDispatcher) {
+ return new Disconnect(rpcDefinition.getQName(), getInputDefinition(rpcDefinition),
+ getOutputDefinition(rpcDefinition), commandDispatcher, rpcDefinition.getDescription());
+ }
+}
--- /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.netconf.cli.commands.local;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.commands.AbstractCommand;
+import org.opendaylight.controller.netconf.cli.commands.Command;
+import org.opendaylight.controller.netconf.cli.commands.CommandDispatcher;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+/**
+ * Local Help command. Displays all commands with description.
+ */
+public class Help extends AbstractCommand {
+
+ private final CommandDispatcher commandDispatcher;
+
+ public Help(final QName qName, final InputDefinition argsDefinition, final OutputDefinition output, final String description, final CommandDispatcher commandDispatcher) {
+ super(qName, argsDefinition, output, description);
+ this.commandDispatcher = commandDispatcher;
+ }
+
+ @Override
+ public Output invoke(final Input inputArgs) {
+ final ArrayList<Node<?>> value = Lists.newArrayList();
+
+ for (final String id : commandDispatcher.getCommandIds()) {
+ final Optional<Command> cmd = commandDispatcher.getCommand(id);
+ Preconditions.checkState(cmd.isPresent(), "Command %s has to be present in command dispatcher", id);
+ final Optional<String> description = cmd.get().getCommandDescription();
+ final List<Node<?>> nameAndDescription = Lists.newArrayList();
+ nameAndDescription.add(NodeFactory.createImmutableSimpleNode(QName.create(getCommandId(), "id"), null, id));
+ if(description.isPresent()) {
+ nameAndDescription.add(NodeFactory.createImmutableSimpleNode(QName.create(getCommandId(), "description"), null, description.get()));
+ }
+ value.add(ImmutableCompositeNode.create(QName.create(getCommandId(), "commands"), nameAndDescription));
+ }
+
+ return new Output(new CompositeNodeTOImpl(getCommandId(), null, value));
+ }
+
+ public static Command create(final RpcDefinition rpcDefinition, final CommandDispatcher commandDispatcher) {
+ return new Help(rpcDefinition.getQName(), getInputDefinition(rpcDefinition), getOutputDefinition(rpcDefinition), rpcDefinition.getDescription(), commandDispatcher);
+ }
+}
--- /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.netconf.cli.commands.output;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Output values for and rpc/command execution
+ */
+public class Output {
+
+ private final CompositeNode output;
+
+ public Output(final CompositeNode output) {
+ this.output = output;
+ }
+
+ public Map<DataSchemaNode, List<Node<?>>> unwrap(final OutputDefinition outputDefinition) {
+ Preconditions.checkArgument(outputDefinition.isEmpty() == false);
+
+ final Map<QName, DataSchemaNode> mappedSchemaNodes = mapOutput(outputDefinition);
+ final Map<DataSchemaNode, List<Node<?>>> mappedNodesToSchema = Maps.newHashMap();
+
+ for (final Node<?> node : output.getValue()) {
+ final DataSchemaNode schemaNode = mappedSchemaNodes.get(node.getKey().withoutRevision());
+ final List<Node<?>> list = mappedNodesToSchema.get(schemaNode) == null ? Lists.<Node<?>> newArrayList()
+ : mappedNodesToSchema.get(schemaNode);
+ list.add(node);
+ mappedNodesToSchema.put(schemaNode, list);
+ }
+
+ return mappedNodesToSchema;
+ }
+
+ public CompositeNode getOutput() {
+ return output;
+ }
+
+ private Map<QName, DataSchemaNode> mapOutput(final OutputDefinition outputDefinition) {
+ final Map<QName, DataSchemaNode> mapped = Maps.newHashMap();
+ for (final DataSchemaNode dataSchemaNode : outputDefinition) {
+ // without revision since data QNames come without revision
+ mapped.put(dataSchemaNode.getQName().withoutRevision(), dataSchemaNode);
+ }
+
+ return mapped;
+ }
+}
--- /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.netconf.cli.commands.output;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * The definition of output elements represented by schema nodes parsed from yang rpc definition
+ */
+public class OutputDefinition implements Iterable<DataSchemaNode> {
+
+ public static final OutputDefinition EMPTY_OUTPUT = new OutputDefinition(Collections.<DataSchemaNode>emptySet());
+ private final Set<DataSchemaNode> childNodes;
+
+ public OutputDefinition(final Set<DataSchemaNode> childNodes) {
+ this.childNodes = childNodes;
+ }
+
+ @Override
+ public Iterator<DataSchemaNode> iterator() {
+ return childNodes.iterator();
+ }
+
+ public static OutputDefinition fromOutput(final ContainerSchemaNode output) {
+ Preconditions.checkNotNull(output);
+ return new OutputDefinition(output.getChildNodes());
+ }
+
+ public static OutputDefinition empty() {
+ return EMPTY_OUTPUT;
+ }
+
+ public boolean isEmpty() {
+ return this == EMPTY_OUTPUT;
+ }
+
+}
--- /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.netconf.cli.commands.remote;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.opendaylight.controller.netconf.cli.commands.AbstractCommand;
+import org.opendaylight.controller.netconf.cli.commands.Command;
+import org.opendaylight.controller.netconf.cli.commands.CommandInvocationException;
+import org.opendaylight.controller.netconf.cli.commands.input.Input;
+import org.opendaylight.controller.netconf.cli.commands.input.InputDefinition;
+import org.opendaylight.controller.netconf.cli.commands.output.Output;
+import org.opendaylight.controller.netconf.cli.commands.output.OutputDefinition;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+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.model.api.RpcDefinition;
+
+/**
+ * Generic remote command implementation that sends the rpc xml to the remote device and waits for response
+ * Waiting is limited with TIMEOUT
+ */
+public class RemoteCommand extends AbstractCommand {
+
+ // TODO make this configurable
+ private static final long DEFAULT_TIMEOUT = 10000;
+ private static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.MILLISECONDS;
+ private final RpcImplementation rpc;
+
+ public RemoteCommand(final QName qName, final InputDefinition args, final OutputDefinition output, final String description, final RpcImplementation rpc) {
+ super(qName, args, output, description);
+ this.rpc = rpc;
+ }
+
+ @Override
+ public Output invoke(final Input inputArgs) throws CommandInvocationException {
+ final ListenableFuture<RpcResult<CompositeNode>> invokeRpc = rpc.invokeRpc(getCommandId(), inputArgs.wrap(getCommandId()));
+ try {
+ return new Output(invokeRpc.get(DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT).getResult());
+ } catch (final ExecutionException e) {
+ throw new CommandInvocationException(getCommandId(), e);
+ } catch (final TimeoutException e) {
+ // Request timed out, cancel request
+ invokeRpc.cancel(true);
+ throw new CommandInvocationException.CommandTimeoutException(getCommandId(), e);
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Command fromRpc(final RpcDefinition rpcDefinition, final RpcImplementation rpcInvoker) {
+ final InputDefinition args = getInputDefinition(rpcDefinition);
+ final OutputDefinition retVal = getOutputDefinition(rpcDefinition);
+
+ return new RemoteCommand(rpcDefinition.getQName(), args, retVal, rpcDefinition.getDescription(), rpcInvoker);
+ }
+}
--- /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.netconf.cli.io;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import jline.console.completer.AggregateCompleter;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class BaseConsoleContext<T extends DataSchemaNode> implements ConsoleContext {
+
+ private static final Completer SKIP_COMPLETER = new StringsCompleter(IOUtil.SKIP);
+
+ private final T dataSchemaNode;
+
+ public BaseConsoleContext(final T dataSchemaNode) {
+ Preconditions.checkNotNull(dataSchemaNode);
+ this.dataSchemaNode = dataSchemaNode;
+ }
+
+ @Override
+ public Completer getCompleter() {
+ final ArrayList<Completer> completers = Lists.newArrayList(SKIP_COMPLETER);
+ completers.addAll(getAdditionalCompleters());
+ return new AggregateCompleter(completers);
+ }
+
+ protected List<Completer> getAdditionalCompleters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.of(dataSchemaNode.getQName().getLocalName());
+ }
+
+ protected T getDataSchemaNode() {
+ return dataSchemaNode;
+ }
+}
--- /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.netconf.cli.io;
+
+import com.google.common.base.Optional;
+import jline.console.completer.Completer;
+
+/**
+ * Context to be set in the IO. Different prompts + completers are required in different contexts of the CLI.
+ */
+public interface ConsoleContext {
+
+ Completer getCompleter();
+
+ Optional<String> getPrompt();
+
+}
--- /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.netconf.cli.io;
+
+import java.io.IOException;
+
+/**
+ * Definition of IO interface
+ */
+public interface ConsoleIO {
+
+ String read() throws IOException;
+
+ String read(Character mask) throws IOException;
+
+ void write(CharSequence data) throws IOException;
+
+ void writeLn(CharSequence data) throws IOException;
+
+ void formatLn(String format, Object... args) throws IOException;
+
+ void enterContext(ConsoleContext consoleContext);
+
+ void enterRootContext(ConsoleContext consoleContext);
+
+ void leaveContext();
+
+ void leaveRootContext();
+
+ void complete();
+
+}
--- /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.netconf.cli.io;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.PATH_SEPARATOR;
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.PROMPT_SUFIX;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import jline.console.ConsoleReader;
+import jline.console.completer.Completer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Jline based IO implementation
+ */
+public class ConsoleIOImpl implements ConsoleIO {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ConsoleIOImpl.class);
+
+ private final ConsoleReader console;
+ private final Deque<ConsoleContext> contexts = new ArrayDeque<>();
+
+ public ConsoleIOImpl() throws IOException {
+ console = new ConsoleReader();
+ console.setHandleUserInterrupt(true);
+ console.setPaginationEnabled(true);
+ console.setHistoryEnabled(true);
+
+ // TODO trifferedActions not supported by jline in current version
+ // https://github.com/jline/jline2/issues/149
+ console.addTriggeredAction('?', new QuestionMarkActionListener());
+ }
+
+ @Override
+ public String read() throws IOException {
+ return console.readLine().trim();
+ }
+
+ @Override
+ public String read(final Character mask) throws IOException {
+ return console.readLine(mask).trim();
+ }
+
+ @Override
+ public void write(final CharSequence data) throws IOException {
+ console.print(data);
+ console.flush();
+ }
+
+ @Override
+ public void writeLn(final CharSequence data) throws IOException {
+ console.println(data);
+ console.flush();
+ }
+
+ @Override
+ public void formatLn(final String format, final Object... args) throws IOException {
+ console.println(String.format(format, args));
+ console.flush();
+ }
+
+ @Override
+ public void enterContext(final ConsoleContext consoleContext) {
+ contexts.push(consoleContext);
+ enterCtx(consoleContext);
+ }
+
+
+ @Override
+ public void enterRootContext(final ConsoleContext consoleContext) {
+ contexts.addLast(consoleContext);
+ enterCtx(consoleContext);
+ }
+
+ private void enterCtx(final ConsoleContext consoleContext) {
+ setCompleter(consoleContext.getCompleter());
+ console.setPrompt(buildPrompt());
+ }
+
+ @Override
+ public void leaveContext() {
+ contexts.pollFirst();
+ leaveCtx();
+ }
+
+ @Override
+ public void leaveRootContext() {
+ contexts.pollLast();
+ leaveCtx();
+ }
+
+ private void leaveCtx() {
+ console.setPrompt(buildPrompt());
+ if (contexts.peek() != null) {
+ setCompleter(contexts.peek().getCompleter());
+ }
+ }
+
+ protected String buildPrompt() {
+ final StringBuilder newPrompt = new StringBuilder();
+
+ final Iterator<ConsoleContext> descendingIterator = contexts.descendingIterator();
+ while (descendingIterator.hasNext()) {
+ final ConsoleContext consoleContext = descendingIterator.next();
+ final Optional<String> promptPart = consoleContext.getPrompt();
+ if (promptPart.isPresent()) {
+ newPrompt.append(PATH_SEPARATOR);
+ newPrompt.append(promptPart.get());
+ }
+ }
+ if (newPrompt.length() ==0) {
+ newPrompt.append(PATH_SEPARATOR);
+ }
+
+ newPrompt.append(PROMPT_SUFIX);
+
+ return newPrompt.toString();
+ }
+
+ private void setCompleter(final Completer newCompleter) {
+ for (final Completer concreteCompleter : console.getCompleters()) {
+ console.removeCompleter(concreteCompleter);
+ }
+ console.addCompleter(newCompleter);
+ }
+
+ private class QuestionMarkActionListener implements ActionListener {
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ ConsoleIOImpl.this.complete();
+ }
+ }
+
+ public void complete() {
+ final ArrayList<CharSequence> candidates = Lists.newArrayList();
+ contexts.peek().getCompleter().complete("", 0, candidates);
+ try {
+ console.getCompletionHandler().complete(console, candidates, 0);
+ } catch (final IOException ex) {
+ throw new IllegalStateException("Unable to write to output", ex);
+ }
+ }
+}
--- /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.netconf.cli.io;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public class IOUtil {
+
+ public static final String SKIP = "skip";
+ public static final String PROMPT_SUFIX = ">";
+ public static final String PATH_SEPARATOR = "/";
+
+ private IOUtil() {
+ }
+
+ public static boolean isQName(final String qName) {
+ final Matcher matcher = patternNew.matcher(qName);
+ return matcher.matches();
+ }
+
+ public static Date parseDate(final String revision) {
+ final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+ try {
+ return formatter.parse(revision);
+ } catch (final ParseException e) {
+ throw new IllegalArgumentException("Date not valid", e);
+ }
+ }
+
+ public static String listType(final SchemaNode schemaNode) {
+ if (schemaNode instanceof LeafListSchemaNode) {
+ return "Leaf-list";
+ } else if (schemaNode instanceof ListSchemaNode) {
+ return "List";
+ } else if (schemaNode instanceof LeafSchemaNode) {
+ return "Leaf";
+ }
+ // FIXME throw exception on unexpected state, not null/emptyString
+ return "";
+ }
+
+ public static String qNameToKeyString(final QName qName, final String moduleName) {
+ return String.format("%s(%s)", qName.getLocalName(), moduleName);
+ }
+
+ // TODO test and check regex + review format of string for QName
+ final static Pattern patternNew = Pattern.compile("([^\\)]+)\\(([^\\)]+)\\)");
+
+ public static QName qNameFromKeyString(final String qName, final Map<String, QName> mappedModules)
+ throws ReadingException {
+ final Matcher matcher = patternNew.matcher(qName);
+ if (!matcher.matches()) {
+ final String message = String.format("QName in wrong format: %s should be: %s", qName, patternNew);
+ throw new ReadingException(message);
+ }
+ final QName base = mappedModules.get(matcher.group(2));
+ if (base == null) {
+ final String message = String.format("Module %s cannot be found", matcher.group(2));
+ throw new ReadingException(message);
+ }
+ return QName.create(base, matcher.group(1));
+ }
+
+ public static boolean isSkipInput(final String rawValue) {
+ return rawValue.equals(SKIP);
+ }
+
+}
--- /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.netconf.cli.reader;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import jline.console.completer.Completer;
+import jline.console.completer.NullCompleter;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+
+public abstract class AbstractReader<T extends DataSchemaNode> implements Reader<T> {
+
+ public static final NullContext NULL_CONTEXT = new NullContext();
+
+ // TODO make console private add protected getter
+ protected ConsoleIO console;
+ private final SchemaContext context;
+ private boolean readConfigNode = false;
+
+ public AbstractReader(final ConsoleIO console, final SchemaContext context) {
+ this.console = console;
+ this.context = context;
+ }
+
+ public AbstractReader(final ConsoleIO console, final SchemaContext context, final boolean readConfigNode) {
+ this(console, context);
+ this.readConfigNode = readConfigNode;
+ }
+
+ protected SchemaContext getSchemaContext() {
+ return context;
+ }
+
+ protected ConsoleIO getConsole() {
+ return console;
+ }
+
+ protected boolean getReadConfigNode() {
+ return readConfigNode;
+ }
+
+ @Override
+ public List<Node<?>> read(final T schemaNode) throws ReadingException {
+ if (isReadingWanted(schemaNode)) {
+ final ConsoleContext ctx = getContext(schemaNode);
+ console.enterContext(ctx);
+ try {
+ return readWithContext(schemaNode);
+ } catch (final IOException e) {
+ throw new ReadingException("Unable to read data from input for " + schemaNode.getQName(), e);
+ } finally {
+ console.leaveContext();
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ private boolean isReadingWanted(final DataSchemaNode node) {
+ if (readConfigNode && !node.isConfiguration()) {
+ return false;
+ }
+ return true;
+ }
+
+ // TODO javadoc
+
+ protected abstract List<Node<?>> readWithContext(T schemaNode) throws IOException, ReadingException;
+
+ protected abstract ConsoleContext getContext(T schemaNode);
+
+ protected Optional<String> getDefaultValue(final T schemaNode) {
+ String defaultValue = null;
+ if (schemaNode instanceof LeafSchemaNode) {
+ defaultValue = ((LeafSchemaNode) schemaNode).getDefault();
+ } else if (schemaNode instanceof ChoiceNode) {
+ defaultValue = ((ChoiceNode) schemaNode).getDefaultCase();
+ }
+
+ return Optional.fromNullable(defaultValue);
+ }
+
+ protected boolean isEmptyInput(final String rawValue) {
+ return Strings.isNullOrEmpty(rawValue);
+ }
+
+ protected static boolean isEmptyType(final TypeDefinition<?> type) {
+ return type instanceof EmptyTypeDefinition;
+ }
+
+ private static class NullContext implements ConsoleContext {
+ @Override
+ public Completer getCompleter() {
+ return new NullCompleter();
+ }
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.absent();
+ }
+ }
+
+}
--- /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.netconf.cli.reader;
+
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * marker interface to mark reader which can be used with GenericListReader
+ */
+public interface GenericListEntryReader<T extends DataSchemaNode> extends Reader<T> {
+
+}
--- /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.netconf.cli.reader;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Generic provider(reader) of input arguments for commands
+ */
+public interface Reader<T extends DataSchemaNode> {
+
+ List<Node<?>> read(T schemaNode) throws ReadingException;
+
+}
--- /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.netconf.cli.reader;
+
+public class ReadingException extends Exception {
+
+ private static final long serialVersionUID = -298382323286156591L;
+
+ public ReadingException(final String msg, final Exception e) {
+ super(msg, e);
+ }
+
+ public ReadingException(final String msg) {
+ super(msg);
+ }
+
+ public static class IncorrectValueException extends ReadingException {
+
+ private static final long serialVersionUID = 164168437058431592L;
+
+ public IncorrectValueException(final String msg) {
+ super(msg);
+ }
+
+ }
+}
--- /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.netconf.cli.reader.custom;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import jline.console.completer.Completer;
+import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Custom reader implementation for filter elements in get/get-config rpcs. This
+ * reader overrides the default anyxml reader and reads filter as a schema path.
+ */
+public class ConfigReader extends AbstractReader<DataSchemaNode> {
+
+ public static final String SEPARATOR = "/";
+
+ private final CommandArgHandlerRegistry commandArgHandlerRegistry;
+ private final Map<String, QName> mappedModules;
+ private final Map<URI, QName> mappedModulesNamespace;
+
+ public ConfigReader(final ConsoleIO console, final SchemaContext remoteSchemaContext,
+ final CommandArgHandlerRegistry commandArgHandlerRegistry) {
+ super(console, remoteSchemaContext);
+ this.commandArgHandlerRegistry = commandArgHandlerRegistry;
+
+ mappedModules = Maps.newHashMap();
+ mappedModulesNamespace = Maps.newHashMap();
+ for (final Module module : remoteSchemaContext.getModules()) {
+ final QName moduleQName = QName.create(module.getNamespace(), module.getRevision(), module.getName());
+ mappedModules.put(moduleQName.getLocalName(), moduleQName);
+ mappedModulesNamespace.put(moduleQName.getNamespace(), moduleQName);
+ }
+ }
+
+ // FIXME refactor + unite common code with FilterReader
+
+ @Override
+ protected List<Node<?>> readWithContext(final DataSchemaNode schemaNode) throws IOException, ReadingException {
+ console.writeLn("Config " + schemaNode.getQName().getLocalName());
+ console.writeLn("Submit path of the data to edit. Use TAB for autocomplete");
+
+ final String rawValue = console.read();
+
+ // FIXME isSkip check should be somewhere in abstractReader
+ if (isSkipInput(rawValue) || Strings.isNullOrEmpty(rawValue)) {
+ return Collections.emptyList();
+ }
+
+ final List<QName> filterPartsQNames = Lists.newArrayList();
+
+ for (final String part : rawValue.split(SEPARATOR)) {
+ final QName qName = IOUtil.qNameFromKeyString(part, mappedModules);
+ filterPartsQNames.add(qName);
+ }
+
+ List<Node<?>> previous = readInnerNode(rawValue);
+
+ for (final QName qName : Lists.reverse(filterPartsQNames).subList(1, filterPartsQNames.size())) {
+ previous = Collections.<Node<?>> singletonList(new CompositeNodeTOImpl(qName, null,
+ previous == null ? Collections.<Node<?>> emptyList() : previous));
+ }
+
+ final Node<?> newNode = previous == null ? null
+ : new CompositeNodeTOImpl(schemaNode.getQName(), null, previous);
+
+ return Collections.<Node<?>> singletonList(newNode);
+ }
+
+ private List<Node<?>> readInnerNode(final String pathString) throws ReadingException {
+ final Optional<DataSchemaNode> schema = getCurrentNode(getSchemaContext(), pathString);
+ Preconditions.checkState(schema.isPresent(), "Unable to find schema for %s", pathString);
+ return commandArgHandlerRegistry.getGenericReader(getSchemaContext(), true).read(schema.get());
+ }
+
+ @Override
+ protected ConsoleContext getContext(final DataSchemaNode schemaNode) {
+ return new FilterConsoleContext(schemaNode, getSchemaContext());
+ }
+
+ private final class FilterConsoleContext extends BaseConsoleContext<DataSchemaNode> {
+
+ private final SchemaContext remoteSchemaContext;
+
+ public FilterConsoleContext(final DataSchemaNode schemaNode, final SchemaContext remoteSchemaContext) {
+ super(schemaNode);
+ this.remoteSchemaContext = remoteSchemaContext;
+ }
+
+ @Override
+ protected List<Completer> getAdditionalCompleters() {
+ return Collections.<Completer> singletonList(new FilterCompleter(remoteSchemaContext));
+ }
+ }
+
+ private final class FilterCompleter implements Completer {
+
+ private final SchemaContext remoteSchemaContext;
+
+ public FilterCompleter(final SchemaContext remoteSchemaContext) {
+ this.remoteSchemaContext = remoteSchemaContext;
+ }
+
+ @Override
+ public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
+ final int idx = buffer.lastIndexOf(SEPARATOR);
+
+ final Optional<DataSchemaNode> currentNode = getCurrentNode(remoteSchemaContext, buffer);
+ if (currentNode.isPresent() && currentNode.get() instanceof DataNodeContainer) {
+ final Collection<DataSchemaNode> childNodes = ((DataNodeContainer) currentNode.get()).getChildNodes();
+ final Collection<String> transformed = Collections2.transform(childNodes,
+ new Function<DataSchemaNode, String>() {
+ @Override
+ public String apply(final DataSchemaNode input) {
+ return IOUtil.qNameToKeyString(input.getQName(),
+ mappedModulesNamespace.get(input.getQName().getNamespace()).getLocalName());
+ }
+ });
+
+ fillCandidates(buffer.substring(idx + 1), candidates, transformed);
+ }
+
+ return idx == -1 ? 0 : idx + 1;
+ }
+
+ private void fillCandidates(final String buffer, final List<CharSequence> candidates,
+ final Collection<String> transformed) {
+ final SortedSet<String> strings = new TreeSet<>(transformed);
+
+ if (buffer == null) {
+ candidates.addAll(strings);
+ } else {
+ for (final String match : strings.tailSet(buffer)) {
+ if (!match.startsWith(buffer)) {
+ break;
+ }
+ candidates.add(match);
+ }
+ }
+
+ if (candidates.size() == 1) {
+ candidates.set(0, candidates.get(0) + SEPARATOR);
+ }
+ }
+
+ }
+
+ private Optional<DataSchemaNode> getCurrentNode(DataSchemaNode parent, final String buffer) {
+ for (final String part : buffer.split(SEPARATOR)) {
+ if (IOUtil.isQName(part) == false) {
+ return Optional.of(parent);
+ }
+
+ final QName qName;
+ try {
+ qName = IOUtil.qNameFromKeyString(part, mappedModules);
+ } catch (final ReadingException e) {
+ return Optional.of(parent);
+ }
+ if (parent instanceof DataNodeContainer) {
+ parent = ((DataNodeContainer) parent).getDataChildByName(qName);
+ } else {
+ // This should check if we are at the end of buffer ?
+ return Optional.of(parent);
+ }
+ }
+ return Optional.of(parent);
+ }
+
+}
--- /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.netconf.cli.reader.custom;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry;
+import org.opendaylight.controller.netconf.cli.commands.CommandConstants;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.controller.netconf.cli.reader.impl.ChoiceReader;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class EditContentReader extends ChoiceReader {
+
+ public static final QName EDIT_CONTENT_QNAME = QName.create(CommandConstants.NETCONF_BASE_QNAME, "edit-content");
+ public static final QName CONFIG_QNAME = QName.create(EDIT_CONTENT_QNAME, "config");
+
+ // FIXME this could be removed if feature/if-feature are supported
+
+ public EditContentReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry, final SchemaContext schemaContext) {
+ super(console, argumentHandlerRegistry, schemaContext);
+ }
+
+ @Override
+ public List<Node<?>> readWithContext(final ChoiceNode choiceNode) throws IOException, ReadingException {
+ Preconditions.checkState(choiceNode.getQName().equals(EDIT_CONTENT_QNAME), "Unexpected choice %s, expected %s", choiceNode, EDIT_CONTENT_QNAME);
+ final ChoiceCaseNode selectedCase = choiceNode.getCaseNodeByName(CONFIG_QNAME);
+ Preconditions.checkNotNull(selectedCase, "Unexpected choice %s, expected %s that contains %s", choiceNode, EDIT_CONTENT_QNAME, CONFIG_QNAME);
+ return readSelectedCase(selectedCase);
+ }
+
+}
--- /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.netconf.cli.reader.custom;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import jline.console.completer.Completer;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Custom reader implementation for filter elements in get/get-config rpcs. This
+ * reader overrides the default anyxml reader and reads filter as a schema path.
+ */
+public class FilterReader extends AbstractReader<DataSchemaNode> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FilterReader.class);
+
+ public static final String SEPARATOR = "/";
+
+ private final Map<String, QName> mappedModules;
+ private final Map<URI, QName> mappedModulesNamespace;
+
+ public FilterReader(final ConsoleIO console, final SchemaContext remoteSchemaContext) {
+ super(console, remoteSchemaContext);
+
+ mappedModules = Maps.newHashMap();
+ mappedModulesNamespace = Maps.newHashMap();
+ for (final Module module : remoteSchemaContext.getModules()) {
+ final QName moduleQName = QName.create(module.getNamespace(), module.getRevision(), module.getName());
+ mappedModules.put(moduleQName.getLocalName(), moduleQName);
+ mappedModulesNamespace.put(moduleQName.getNamespace(), moduleQName);
+ }
+ }
+
+ // FIXME refactor
+
+ public static final QName FILTER_TYPE_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "2011-06-01",
+ "type");
+ public static final String FILTER_TYPE_VALUE_DEFAULT = "subtree";
+
+ @Override
+ protected List<Node<?>> readWithContext(final DataSchemaNode schemaNode) throws IOException, ReadingException {
+ boolean redSuccessfuly = false;
+ Node<?> newNode = null;
+ do {
+ console.writeLn("Filter " + schemaNode.getQName().getLocalName());
+ console.writeLn("Submit path of the data to retrieve. Use TAB for autocomplete");
+
+ final String rawValue = console.read();
+
+ // FIXME skip should be somewhere in abstractReader
+ if (isSkipInput(rawValue) || Strings.isNullOrEmpty(rawValue)) {
+ return Collections.emptyList();
+ }
+
+ final List<QName> filterPartsQNames = Lists.newArrayList();
+
+ try {
+ for (final String part : rawValue.split(SEPARATOR)) {
+ final QName qName = IOUtil.qNameFromKeyString(part, mappedModules);
+ filterPartsQNames.add(qName);
+ }
+
+ Node<?> previous = null;
+
+ for (final QName qName : Lists.reverse(filterPartsQNames)) {
+ previous = new CompositeNodeTOImpl(qName, null,
+ previous == null ? Collections.<Node<?>> emptyList()
+ : Collections.<Node<?>> singletonList(previous));
+ }
+
+ final Map<QName, String> attributes = Collections.singletonMap(FILTER_TYPE_QNAME,
+ FILTER_TYPE_VALUE_DEFAULT);
+ newNode = previous == null ? null : ImmutableCompositeNode.create(schemaNode.getQName(), attributes,
+ Collections.<Node<?>> singletonList(previous));
+ redSuccessfuly = true;
+ } catch (final ReadingException e) {
+ final String message = "Specified filter path isn't correct.";
+ LOG.error(message, e);
+ console.writeLn(message);
+ }
+ } while (!redSuccessfuly);
+ return Collections.<Node<?>> singletonList(newNode);
+ }
+
+ @Override
+ protected ConsoleContext getContext(final DataSchemaNode schemaNode) {
+ return new FilterConsoleContext(schemaNode, getSchemaContext());
+ }
+
+ private final class FilterConsoleContext extends BaseConsoleContext<DataSchemaNode> {
+
+ private final SchemaContext remoteSchemaContext;
+
+ public FilterConsoleContext(final DataSchemaNode schemaNode, final SchemaContext remoteSchemaContext) {
+ super(schemaNode);
+ this.remoteSchemaContext = remoteSchemaContext;
+ }
+
+ @Override
+ protected List<Completer> getAdditionalCompleters() {
+ return Collections.<Completer> singletonList(new FilterCompleter(remoteSchemaContext));
+ }
+
+ }
+
+ private final class FilterCompleter implements Completer {
+
+ private final SchemaContext remoteSchemaContext;
+
+ // TODO add skip to filter completer, better soulution would be to add
+ // SKIP completer before context completer if possible
+
+ public FilterCompleter(final SchemaContext remoteSchemaContext) {
+ this.remoteSchemaContext = remoteSchemaContext;
+ }
+
+ @Override
+ public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
+ final int idx = buffer.lastIndexOf(SEPARATOR);
+
+ final Optional<DataNodeContainer> currentNode = getCurrentNode(remoteSchemaContext, buffer);
+ if (currentNode.isPresent()) {
+
+ final Collection<String> transformed = Collections2.transform(currentNode.get().getChildNodes(),
+ new Function<DataSchemaNode, String>() {
+ @Override
+ public String apply(final DataSchemaNode input) {
+ return IOUtil.qNameToKeyString(input.getQName(),
+ mappedModulesNamespace.get(input.getQName().getNamespace()).getLocalName());
+ }
+ });
+
+ fillCandidates(buffer.substring(idx + 1), candidates, transformed);
+ }
+
+ return idx == -1 ? 0 : idx + 1;
+ }
+
+ private void fillCandidates(final String buffer, final List<CharSequence> candidates,
+ final Collection<String> transformed) {
+ final SortedSet<String> strings = new TreeSet<>(transformed);
+
+ if (buffer == null) {
+ candidates.addAll(strings);
+ } else {
+ for (final String match : strings.tailSet(buffer)) {
+ if (!match.startsWith(buffer)) {
+ break;
+ }
+ candidates.add(match);
+ }
+ }
+
+ if (candidates.size() == 1) {
+ candidates.set(0, candidates.get(0) + SEPARATOR);
+ }
+ }
+
+ private Optional<DataNodeContainer> getCurrentNode(DataNodeContainer parent, final String buffer) {
+ for (final String part : buffer.split(SEPARATOR)) {
+ if (!IOUtil.isQName(part)) {
+ return Optional.of(parent);
+ }
+
+ QName qName;
+ try {
+ qName = IOUtil.qNameFromKeyString(part, mappedModules);
+ } catch (final ReadingException e) {
+ return Optional.of(parent);
+ }
+
+ final DataSchemaNode dataChildByName = parent.getDataChildByName(qName);
+ if (dataChildByName instanceof DataNodeContainer) {
+ parent = (DataNodeContainer) dataChildByName;
+ } else {
+ return Optional.absent();
+ }
+ }
+ return Optional.of(parent);
+ }
+ }
+
+}
--- /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.netconf.cli.reader.custom;
+
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import jline.console.completer.Completer;
+import jline.console.completer.NullCompleter;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.impl.BasicDataHolderReader;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public class PasswordReader extends BasicDataHolderReader<DataSchemaNode> {
+
+ private static final char PASSWORD_MASK = '*';
+
+ public PasswordReader(final ConsoleIO console, final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ }
+
+ @Override
+ protected ConsoleContext getContext(final DataSchemaNode schemaNode) {
+ return new BaseConsoleContext<DataSchemaNode>(schemaNode) {
+ @Override
+ public Completer getCompleter() {
+ return new NullCompleter();
+ }
+ };
+ }
+
+ @Override
+ protected TypeDefinition<?> getType(final DataSchemaNode schemaNode) {
+ Preconditions.checkArgument(schemaNode instanceof LeafSchemaNode);
+ return ((LeafSchemaNode)schemaNode).getType();
+ }
+
+ @Override
+ protected String readValue() throws IOException {
+ return console.read(PASSWORD_MASK);
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.listType;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public class AnyXmlReader extends AbstractReader<AnyXmlSchemaNode> {
+
+ public AnyXmlReader(final ConsoleIO console, final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ }
+
+ public AnyXmlReader(final ConsoleIO console, final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ }
+
+ @Override
+ protected List<Node<?>> readWithContext(final AnyXmlSchemaNode schemaNode) throws IOException, ReadingException {
+ console.writeLn(listType(schemaNode) + " " + schemaNode.getQName().getLocalName());
+
+ final String rawValue = console.read();
+
+ Node<?> newNode = null;
+ if (!isSkipInput(rawValue)) {
+ final Optional<Node<?>> value = tryParse(rawValue);
+
+ if (value.isPresent()) {
+ newNode = NodeFactory.createImmutableCompositeNode(schemaNode.getQName(), null,
+ Collections.<Node<?>> singletonList(value.get()));
+ } else {
+ newNode = NodeFactory.createImmutableSimpleNode(schemaNode.getQName(), null, rawValue);
+ }
+ }
+
+ final List<Node<?>> newNodes = new ArrayList<>();
+ newNodes.add(newNode);
+ return newNodes;
+ }
+
+ private Optional<Node<?>> tryParse(final String rawValue) {
+ try {
+ final Document dom = XmlUtil.readXmlToDocument(rawValue);
+ return Optional.<Node<?>> of(XmlDocumentUtils.toDomNode(dom));
+ } catch (SAXException | IOException e) {
+ // TODO log
+ return Optional.absent();
+ }
+ }
+
+ @Override
+ protected ConsoleContext getContext(final AnyXmlSchemaNode schemaNode) {
+ return new BaseConsoleContext<>(schemaNode);
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.listType;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.HashBiMap;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class BasicDataHolderReader<T extends DataSchemaNode> extends AbstractReader<T> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BasicDataHolderReader.class);
+ private DataHolderCompleter currentCompleter;
+
+ public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext,
+ final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ }
+
+ public BasicDataHolderReader(final ConsoleIO console, final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ }
+
+ @Override
+ public List<Node<?>> readWithContext(final T schemaNode) throws IOException, ReadingException {
+ TypeDefinition<?> type = getType(schemaNode);
+ console.formatLn("Submit %s %s(%s)", listType(schemaNode), schemaNode.getQName().getLocalName(), type.getQName().getLocalName());
+
+ while (baseTypeFor(type) instanceof UnionTypeDefinition) {
+ final Optional<TypeDefinition<?>> optionalTypeDef = new UnionTypeReader(console).read(type);
+ if (!optionalTypeDef.isPresent()) {
+ return postSkipOperations(schemaNode);
+ }
+ type = optionalTypeDef.get();
+ }
+
+ if (currentCompleter == null) {
+ currentCompleter = getBaseCompleter(schemaNode);
+ }
+
+ // TODO what if type is leafref, instance-identifier?
+
+ // Handle empty type leaf by question
+ if (isEmptyType(type)) {
+ final Optional<Boolean> shouldAddEmpty = new DecisionReader().read(console, "Add empty type leaf %s ?",
+ schemaNode.getQName().getLocalName());
+ if (shouldAddEmpty.isPresent()) {
+ if (shouldAddEmpty.get()) {
+ return wrapValue(schemaNode, "");
+ } else {
+ return Collections.emptyList();
+ }
+ } else {
+ return postSkipOperations(schemaNode);
+ }
+ }
+
+ final String rawValue = readValue();
+ if (isSkipInput(rawValue)) {
+ return postSkipOperations(schemaNode);
+ }
+
+ final Object resolvedValue = currentCompleter.resolveValue(rawValue);
+
+ // Reset state TODO should be in finally
+ currentCompleter = null;
+ return wrapValue(schemaNode, resolvedValue);
+ }
+
+ private List<Node<?>> postSkipOperations(final DataSchemaNode schemaNode) throws IOException {
+ console.formatLn("Skipping %s", schemaNode.getQName());
+ return Collections.emptyList();
+ }
+
+ private TypeDefinition<?> baseTypeFor(final TypeDefinition<?> type) {
+ if (type.getBaseType() != null) {
+ return baseTypeFor(type.getBaseType());
+ }
+ return type;
+ }
+
+ protected String readValue() throws IOException {
+ return console.read();
+ }
+
+ private List<Node<?>> wrapValue(final T schemaNode, final Object value) {
+ final Node<?> newNode = NodeFactory.createImmutableSimpleNode(schemaNode.getQName(), null, value);
+ return Collections.<Node<?>> singletonList(newNode);
+ }
+
+ protected abstract TypeDefinition<?> getType(final T schemaNode);
+
+ protected final DataHolderCompleter getBaseCompleter(final T schemaNode) {
+ final TypeDefinition<?> type = getType(schemaNode);
+ final DataHolderCompleter currentCompleter;
+
+ // Add enum completer
+ if (type instanceof EnumTypeDefinition) {
+ currentCompleter = new EnumDataHolderCompleter(type);
+ } else if (type instanceof IdentityrefTypeDefinition) {
+ currentCompleter = new IdentityRefDataHolderCompleter(type, getSchemaContext());
+ } else {
+ currentCompleter = new GeneralDataHolderCompleter(type);
+ }
+ this.currentCompleter = currentCompleter;
+ return currentCompleter;
+ }
+
+ private static interface DataHolderCompleter extends Completer {
+
+ Object resolveValue(String rawValue) throws ReadingException;
+ }
+
+ private static class GeneralDataHolderCompleter implements DataHolderCompleter {
+
+ private final Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> codec;
+ private final TypeDefinition<?> type;
+
+ public GeneralDataHolderCompleter(final TypeDefinition<?> type) {
+ this.type = type;
+ codec = getCodecForType(type);
+ }
+
+ protected TypeDefinition<?> getType() {
+ return type;
+ }
+
+ private Optional<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> getCodecForType(
+ final TypeDefinition<?> type) {
+ if (type != null) {
+ return Optional
+ .<TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>>> fromNullable(TypeDefinitionAwareCodec
+ .from(type));
+ }
+ return Optional.absent();
+ }
+
+ @Override
+ public Object resolveValue(final String rawValue) throws ReadingException {
+ try {
+ return codec.isPresent() ? codec.get().deserialize(rawValue) : rawValue;
+ } catch (final RuntimeException e) {
+ final String message = "It wasn't possible deserialize value " + rawValue + ".";
+ LOG.error(message, e);
+ throw new ReadingException(message, e);
+ }
+ }
+
+ @Override
+ public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
+ return 0;
+ }
+ }
+
+ private static final class EnumDataHolderCompleter extends GeneralDataHolderCompleter {
+
+ public EnumDataHolderCompleter(final TypeDefinition<?> type) {
+ super(type);
+ }
+
+ @Override
+ public Object resolveValue(final String rawValue) throws ReadingException {
+ return super.resolveValue(rawValue);
+ }
+
+ @Override
+ public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
+ return new StringsCompleter(Collections2.transform(((EnumTypeDefinition) getType()).getValues(),
+ new Function<EnumPair, String>() {
+ @Override
+ public String apply(final EnumPair input) {
+ return input.getName();
+ }
+ })).complete(buffer, cursor, candidates);
+ }
+ }
+
+ private static final class IdentityRefDataHolderCompleter extends GeneralDataHolderCompleter {
+
+ private final BiMap<String, QName> identityMap;
+
+ public IdentityRefDataHolderCompleter(final TypeDefinition<?> type, final SchemaContext schemaContext) {
+ super(type);
+ this.identityMap = getIdentityMap(schemaContext);
+ }
+
+ private static BiMap<String, QName> getIdentityMap(final SchemaContext schemaContext) {
+ final BiMap<String, QName> identityMap = HashBiMap.create();
+ for (final Module module : schemaContext.getModules()) {
+ for (final IdentitySchemaNode identity : module.getIdentities()) {
+ identityMap.put(getIdentityName(identity, module), identity.getQName());
+ }
+ }
+ return identityMap;
+ }
+
+ private static String getIdentityName(final IdentitySchemaNode rpcDefinition, final Module module) {
+ return IOUtil.qNameToKeyString(rpcDefinition.getQName(), module.getName());
+ }
+
+ @Override
+ public Object resolveValue(final String rawValue) throws ReadingException {
+ final QName qName = identityMap.get(rawValue);
+ if (qName == null) {
+ throw new ReadingException("No identity found for " + rawValue + " available " + identityMap.keySet());
+ }
+ return qName;
+ }
+
+ @Override
+ public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
+
+ return new StringsCompleter(Collections2.transform(((IdentityrefTypeDefinition) getType()).getIdentity()
+ .getDerivedIdentities(), new Function<IdentitySchemaNode, String>() {
+ @Override
+ public String apply(final IdentitySchemaNode input) {
+ return identityMap.inverse().get(input.getQName());
+ }
+ })).complete(buffer, cursor, candidates);
+ }
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChoiceReader extends AbstractReader<ChoiceNode> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ChoiceReader.class);
+
+ private final CommandArgHandlerRegistry argumentHandlerRegistry;
+
+ public ChoiceReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ public ChoiceReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ @Override
+ public List<Node<?>> readWithContext(final ChoiceNode choiceNode) throws IOException, ReadingException {
+ final Map<String, ChoiceCaseNode> availableCases = collectAllCases(choiceNode);
+ console.formatLn("Select case for choice %s from: %s", choiceNode.getQName().getLocalName(),
+ formatSet(availableCases.keySet()));
+
+ ChoiceCaseNode selectedCase = null;
+ final String rawValue = console.read();
+ if (isSkipInput(rawValue)) {
+ return Collections.emptyList();
+ }
+
+ selectedCase = availableCases.get(rawValue);
+ if (selectedCase == null) {
+ final String message = String.format("Incorrect value (%s) for choice %s was selected.", rawValue,
+ choiceNode.getQName().getLocalName());
+ LOG.error(message);
+ throw new ReadingException(message);
+ }
+
+ return readSelectedCase(selectedCase);
+ }
+
+ protected List<Node<?>> readSelectedCase(final ChoiceCaseNode selectedCase) throws ReadingException {
+ // IF there is a case that contains only one Empty type leaf, create the
+ // leaf without question, since the case was selected
+ if (containsOnlyOneEmptyLeaf(selectedCase)) {
+ final Node<?> newNode = NodeFactory.createImmutableSimpleNode(selectedCase.getChildNodes().iterator()
+ .next().getQName(), null, null);
+ return Collections.<Node<?>> singletonList(newNode);
+ }
+
+ final List<Node<?>> newNodes = new ArrayList<>();
+ for (final DataSchemaNode schemaNode : selectedCase.getChildNodes()) {
+ newNodes.addAll(argumentHandlerRegistry.getGenericReader(getSchemaContext(), getReadConfigNode()).read(
+ schemaNode));
+ }
+ return newNodes;
+ }
+
+ private Object formatSet(final Set<String> values) {
+ final StringBuilder formatedValues = new StringBuilder();
+ for (final String value : values) {
+ formatedValues.append("\n ");
+ formatedValues.append(value);
+ }
+ return formatedValues.toString();
+ }
+
+ private boolean containsOnlyOneEmptyLeaf(final ChoiceCaseNode selectedCase) {
+ if (selectedCase.getChildNodes().size() != 1) {
+ return false;
+ }
+ final DataSchemaNode next = selectedCase.getChildNodes().iterator().next();
+ if (next instanceof LeafSchemaNode) {
+ final TypeDefinition<?> type = ((LeafSchemaNode) next).getType();
+ if (isEmptyType(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Map<String, ChoiceCaseNode> collectAllCases(final ChoiceNode schemaNode) {
+ return Maps.uniqueIndex(schemaNode.getCases(), new Function<ChoiceCaseNode, String>() {
+ @Override
+ public String apply(final ChoiceCaseNode input) {
+ return input.getQName().getLocalName();
+ }
+ });
+ }
+
+ @Override
+ protected ConsoleContext getContext(final ChoiceNode schemaNode) {
+ return new BaseConsoleContext<ChoiceNode>(schemaNode) {
+ @Override
+ public List<Completer> getAdditionalCompleters() {
+ return Collections
+ .<Completer> singletonList(new StringsCompleter(collectAllCases(schemaNode).keySet()));
+ }
+ };
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class ContainerReader extends AbstractReader<ContainerSchemaNode> {
+
+ private final CommandArgHandlerRegistry argumentHandlerRegistry;
+ private static final InputArgsLocalNameComparator CONTAINER_CHILDS_SORTER = new InputArgsLocalNameComparator();
+
+ public ContainerReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ public ContainerReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ @Override
+ public List<Node<?>> readWithContext(final ContainerSchemaNode containerNode) throws IOException, ReadingException {
+ console.formatLn("Submit child nodes for container: %s, %s", containerNode.getQName().getLocalName(),
+ Collections2.transform(containerNode.getChildNodes(), new Function<DataSchemaNode, String>() {
+ @Override
+ public String apply(final DataSchemaNode input) {
+ return input.getQName().getLocalName();
+ }
+ }));
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> compositeNodeBuilder = ImmutableCompositeNode.builder();
+ compositeNodeBuilder.setQName(containerNode.getQName());
+ final SeparatedNodes separatedNodes = SeparatedNodes.separateNodes(containerNode, getReadConfigNode());
+ for (final DataSchemaNode childNode : sortChildren(separatedNodes.getMandatoryNotKey())) {
+ final List<Node<?>> redNodes = argumentHandlerRegistry.getGenericReader(getSchemaContext(),
+ getReadConfigNode()).read(childNode);
+ if (redNodes.isEmpty()) {
+ console.formatLn("No data specified for mandatory element %s.", childNode.getQName().getLocalName());
+ return Collections.emptyList();
+ } else {
+ compositeNodeBuilder.addAll(redNodes);
+ }
+ }
+
+ for (final DataSchemaNode childNode : sortChildren(separatedNodes.getOthers())) {
+ compositeNodeBuilder.addAll(argumentHandlerRegistry.getGenericReader(getSchemaContext(),
+ getReadConfigNode()).read(childNode));
+ }
+ return Collections.<Node<?>> singletonList(compositeNodeBuilder.toInstance());
+ }
+
+ private List<DataSchemaNode> sortChildren(final Set<DataSchemaNode> unsortedNodes) {
+ final List<DataSchemaNode> childNodes = Lists.newArrayList(unsortedNodes);
+ Collections.sort(childNodes, CONTAINER_CHILDS_SORTER);
+ return childNodes;
+ }
+
+ @Override
+ protected ConsoleContext getContext(final ContainerSchemaNode schemaNode) {
+ return new BaseConsoleContext<>(schemaNode);
+ }
+
+ private static class InputArgsLocalNameComparator implements Comparator<DataSchemaNode> {
+ @Override
+ public int compare(final DataSchemaNode o1, final DataSchemaNode o2) {
+ return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
+ }
+ }
+
+}
--- /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.netconf.cli.reader.impl;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.SKIP;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import jline.console.completer.AggregateCompleter;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import jline.internal.Log;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+
+public class DecisionReader {
+
+ private static final String YES = "Y";
+ private static final String NO = "N";
+ public static final Completer YES_NO_COMPLETER = new StringsCompleter(YES, NO);
+
+ public Optional<Boolean> read(final ConsoleIO console, final String questionMessageBlueprint,
+ final Object... questionMessageArgs) throws IOException, ReadingException {
+ final ConsoleContext ctx = getContext();
+ console.enterContext(ctx);
+ try {
+ console.formatLn(questionMessageBlueprint, questionMessageArgs);
+ final String rawValue = console.read();
+ if (YES.equals(rawValue.toUpperCase())) {
+ return Optional.of(Boolean.TRUE);
+ } else if (NO.equals(rawValue.toUpperCase())) {
+ return Optional.of(Boolean.FALSE);
+ } else if (SKIP.equals(rawValue)) {
+ return Optional.absent();
+ } else {
+ final String message = String.format("Incorrect possibility (%s) was selected", rawValue);
+ Log.error(message);
+ throw new ReadingException(message);
+ }
+ } finally {
+ console.leaveContext();
+ }
+ }
+
+ private static ConsoleContext getContext() {
+ return new ConsoleContext() {
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Completer getCompleter() {
+ return new AggregateCompleter(YES_NO_COMPLETER, new StringsCompleter(IOUtil.SKIP));
+ }
+
+ };
+ }
+
+}
--- /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.netconf.cli.reader.impl;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.listType;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.GenericListEntryReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GenericListReader<T extends DataSchemaNode> extends AbstractReader<T> {
+ private static final Logger LOG = LoggerFactory.getLogger(GenericListReader.class);
+
+ private final GenericListEntryReader<T> concreteListEntryReader;
+
+ public GenericListReader(final ConsoleIO console, final GenericListEntryReader<T> concreteListEntryReader,
+ final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ this.concreteListEntryReader = concreteListEntryReader;
+ }
+
+ public GenericListReader(final ConsoleIO console, final GenericListEntryReader<T> concreteListEntryReader,
+ final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ this.concreteListEntryReader = concreteListEntryReader;
+ }
+
+ @Override
+ public List<Node<?>> readWithContext(final T schemaNode) throws IOException, ReadingException {
+ final List<Node<?>> newNodes = new ArrayList<>();
+ Optional<Boolean> readNextListEntry = Optional.of(Boolean.TRUE);
+ console.formatLn("Reading collection type argument: %s", schemaNode.getQName().getLocalName());
+ while (readNextListEntry.isPresent() && readNextListEntry.get()) {
+ try {
+ newNodes.addAll(concreteListEntryReader.read(schemaNode));
+ } catch (final ReadingException e) {
+ console.writeLn(e.getMessage());
+ }
+ readNextListEntry = new DecisionReader().read(console, "Add other entry to " + listType(schemaNode) + " "
+ + schemaNode.getQName().getLocalName() + " " + " [Y|N]?");
+ }
+ console.formatLn("Collection type argument: %s read finished", schemaNode.getQName().getLocalName());
+
+ return newNodes;
+ }
+
+ @Override
+ protected ConsoleContext getContext(final T schemaNode) {
+ return new BaseConsoleContext<>(schemaNode);
+ }
+
+}
--- /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.netconf.cli.reader.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry;
+import org.opendaylight.controller.netconf.cli.commands.CommandConstants;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.GenericListEntryReader;
+import org.opendaylight.controller.netconf.cli.reader.Reader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public class GenericReader extends AbstractReader<DataSchemaNode> {
+
+ private final CommandArgHandlerRegistry argumentHandlerRegistry;
+
+ public GenericReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ public GenericReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ @Override
+ protected List<Node<?>> readWithContext(final DataSchemaNode schemaNode) throws IOException, ReadingException {
+ final Optional<Class<? extends Reader<DataSchemaNode>>> customReaderClassOpt = tryGetCustomHandler(schemaNode);
+
+ if (customReaderClassOpt.isPresent()) {
+ // TODO resolve class cast of generic custom readers
+ final Reader<DataSchemaNode> customReaderInstance = (Reader<DataSchemaNode>) argumentHandlerRegistry
+ .getCustomReader(customReaderClassOpt.get());
+ Preconditions.checkNotNull(customReaderInstance, "Unknown custom reader: %s", customReaderClassOpt.get());
+ return customReaderInstance.read(schemaNode);
+ } else {
+ return readGeneric(schemaNode);
+ }
+
+ // TODO reuse instances
+ }
+
+ private List<Node<?>> readGeneric(final DataSchemaNode schemaNode) throws ReadingException, IOException {
+ final List<Node<?>> newNodes = new ArrayList<>();
+ boolean isRedCorrectly = false;
+ do {
+ try {
+ if (schemaNode instanceof LeafSchemaNode) {
+ return new LeafReader(console, getSchemaContext(), getReadConfigNode())
+ .read((LeafSchemaNode) schemaNode);
+ } else if (schemaNode instanceof ContainerSchemaNode) {
+ return new ContainerReader(console, argumentHandlerRegistry, getSchemaContext(),
+ getReadConfigNode()).read((ContainerSchemaNode) schemaNode);
+ } else if (schemaNode instanceof ListSchemaNode) {
+ final GenericListEntryReader<ListSchemaNode> entryReader = new ListEntryReader(console,
+ argumentHandlerRegistry, getSchemaContext(), getReadConfigNode());
+ return new GenericListReader<>(console, entryReader, getSchemaContext(), getReadConfigNode())
+ .read((ListSchemaNode) schemaNode);
+ } else if (schemaNode instanceof LeafListSchemaNode) {
+ final GenericListEntryReader<LeafListSchemaNode> entryReader = new LeafListEntryReader(console,
+ getSchemaContext(), getReadConfigNode());
+ return new GenericListReader<>(console, entryReader, getSchemaContext(), getReadConfigNode())
+ .read((LeafListSchemaNode) schemaNode);
+ } else if (schemaNode instanceof ChoiceNode) {
+ return new ChoiceReader(console, argumentHandlerRegistry, getSchemaContext(), getReadConfigNode())
+ .read((ChoiceNode) schemaNode);
+ } else if (schemaNode instanceof AnyXmlSchemaNode) {
+ return new AnyXmlReader(console, getSchemaContext(), getReadConfigNode())
+ .read((AnyXmlSchemaNode) schemaNode);
+ }
+ isRedCorrectly = true;
+ } catch (final ReadingException e) {
+ console.writeLn(e.getMessage());
+ }
+ } while (!isRedCorrectly);
+ return newNodes;
+ }
+
+ @Override
+ protected ConsoleContext getContext(final DataSchemaNode schemaNode) {
+ // return null context, leave context to specific implementations
+ return NULL_CONTEXT;
+ }
+
+ private <T> Optional<Class<? extends T>> tryGetCustomHandler(final DataSchemaNode dataSchemaNode) {
+
+ for (final UnknownSchemaNode unknownSchemaNode : dataSchemaNode.getUnknownSchemaNodes()) {
+
+ if (isExtenstionForCustomHandler(unknownSchemaNode)) {
+ final String argumentHandlerClassName = unknownSchemaNode.getNodeParameter();
+ try {
+ final Class<?> argumentClass = Class.forName(argumentHandlerClassName);
+ // TODO add check before cast
+ return Optional.<Class<? extends T>> of((Class<? extends T>) argumentClass);
+ } catch (final ClassNotFoundException e) {
+ throw new IllegalArgumentException("Unknown custom reader class " + argumentHandlerClassName
+ + " for: " + dataSchemaNode.getQName());
+ }
+ }
+ }
+
+ return Optional.absent();
+ }
+
+ private boolean isExtenstionForCustomHandler(final UnknownSchemaNode unknownSchemaNode) {
+ final QName qName = unknownSchemaNode.getExtensionDefinition().getQName();
+ return qName.equals(CommandConstants.ARG_HANDLER_EXT_QNAME);
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import java.util.List;
+import jline.console.completer.Completer;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.GenericListEntryReader;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+class LeafListEntryReader extends BasicDataHolderReader<LeafListSchemaNode> implements
+ GenericListEntryReader<LeafListSchemaNode> {
+
+ public LeafListEntryReader(final ConsoleIO console, final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ }
+
+ public LeafListEntryReader(final ConsoleIO console, final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ }
+
+ @Override
+ protected TypeDefinition<?> getType(final LeafListSchemaNode schemaNode) {
+ return schemaNode.getType();
+ }
+
+ @Override
+ protected ConsoleContext getContext(final LeafListSchemaNode schemaNode) {
+ return new BaseConsoleContext<LeafListSchemaNode>(schemaNode) {
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.of("[entry]");
+ }
+
+ @Override
+ protected List<Completer> getAdditionalCompleters() {
+ return Lists.<Completer> newArrayList(getBaseCompleter(getDataSchemaNode()));
+ }
+ };
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import java.util.List;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+public class LeafReader extends BasicDataHolderReader<LeafSchemaNode> {
+
+ public LeafReader(final ConsoleIO console, final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ }
+
+ public LeafReader(final ConsoleIO console, final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ }
+
+ @Override
+ protected TypeDefinition<?> getType(final LeafSchemaNode schemaNode) {
+ return schemaNode.getType();
+ }
+
+ @Override
+ protected ConsoleContext getContext(final LeafSchemaNode schemaNode) {
+ return new BaseConsoleContext<LeafSchemaNode>(schemaNode) {
+ @Override
+ public List<Completer> getAdditionalCompleters() {
+ final List<Completer> completers = Lists.<Completer> newArrayList(getBaseCompleter(schemaNode));
+ final Optional<String> defaultValue = getDefaultValue(schemaNode);
+ if (defaultValue.isPresent()) {
+ completers.add(new StringsCompleter(defaultValue.get()));
+ }
+ return completers;
+ }
+ };
+ }
+}
--- /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.netconf.cli.reader.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.Collections2;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.controller.netconf.cli.CommandArgHandlerRegistry;
+import org.opendaylight.controller.netconf.cli.io.BaseConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.reader.AbstractReader;
+import org.opendaylight.controller.netconf.cli.reader.GenericListEntryReader;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class ListEntryReader extends AbstractReader<ListSchemaNode> implements GenericListEntryReader<ListSchemaNode> {
+ private static final Logger LOG = LoggerFactory.getLogger(ListEntryReader.class);
+
+ private final CommandArgHandlerRegistry argumentHandlerRegistry;
+
+ public ListEntryReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext) {
+ super(console, schemaContext);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ public ListEntryReader(final ConsoleIO console, final CommandArgHandlerRegistry argumentHandlerRegistry,
+ final SchemaContext schemaContext, final boolean readConfigNode) {
+ super(console, schemaContext, readConfigNode);
+ this.argumentHandlerRegistry = argumentHandlerRegistry;
+ }
+
+ @Override
+ public List<Node<?>> readWithContext(final ListSchemaNode listNode) throws IOException, ReadingException {
+ console.formatLn("Submit child nodes for list entry: %s, %s", listNode.getQName().getLocalName(),
+ Collections2.transform(listNode.getChildNodes(), new Function<DataSchemaNode, String>() {
+ @Override
+ public String apply(final DataSchemaNode input) {
+ return input.getQName().getLocalName();
+ }
+ }));
+
+ final String listName = listNode.getQName().getLocalName();
+ final CompositeNodeBuilder<ImmutableCompositeNode> compositeNodeBuilder = ImmutableCompositeNode.builder();
+ compositeNodeBuilder.setQName(listNode.getQName());
+
+ final SeparatedNodes separatedChildNodes = SeparatedNodes.separateNodes(listNode, getReadConfigNode());
+
+ final List<Node<?>> nodes = readKeys(separatedChildNodes.getKeyNodes());
+ nodes.addAll(readMandatoryNotKeys(separatedChildNodes.getMandatoryNotKey()));
+ if (!separatedChildNodes.getOthers().isEmpty()) {
+ final Optional<Boolean> readNodesWhichAreNotKey = new DecisionReader().read(console,
+ "Add non-key, non-mandatory nodes to list %s? [Y|N]", listName);
+ if (readNodesWhichAreNotKey.isPresent() && readNodesWhichAreNotKey.get()) {
+ nodes.addAll(readNotKeys(separatedChildNodes.getOthers()));
+ }
+ }
+
+ if (!nodes.isEmpty()) {
+ compositeNodeBuilder.addAll(nodes);
+ return Collections.<Node<?>> singletonList(compositeNodeBuilder.toInstance());
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private List<Node<?>> readKeys(final Set<DataSchemaNode> keys) throws ReadingException, IOException {
+ final List<Node<?>> newNodes = new ArrayList<>();
+ console.writeLn("Reading keys:");
+ for (final DataSchemaNode key : keys) {
+ final List<Node<?>> readKey = new LeafReader(console, getSchemaContext(), getReadConfigNode())
+ .read((LeafSchemaNode) key);
+ if (readKey.size() != 1) {
+ final String message = String.format(
+ "Value for key element %s has to be set. Creation of this entry is canceled.", key.getQName()
+ .getLocalName());
+ LOG.error(message);
+ throw new ReadingException(message);
+ }
+ newNodes.addAll(readKey);
+ }
+ return newNodes;
+ }
+
+ private List<Node<?>> readMandatoryNotKeys(final Set<DataSchemaNode> mandatoryNotKeys) throws ReadingException,
+ IOException {
+ final List<Node<?>> newNodes = new ArrayList<>();
+ console.writeLn("Reading mandatory not keys nodes:");
+
+ for (final DataSchemaNode mandatoryNode : mandatoryNotKeys) {
+ final List<Node<?>> redValue = argumentHandlerRegistry.getGenericReader(getSchemaContext(),
+ getReadConfigNode()).read(mandatoryNode);
+ if (redValue.isEmpty()) {
+ final String message = String.format(
+ "Value for mandatory element %s has to be set. Creation of this entry is canceled.",
+ mandatoryNode.getQName().getLocalName());
+ LOG.error(message);
+ throw new ReadingException(message);
+ }
+ newNodes.addAll(redValue);
+ }
+ return newNodes;
+ }
+
+ private List<Node<?>> readNotKeys(final Set<DataSchemaNode> notKeys) throws ReadingException {
+ final List<Node<?>> newNodes = new ArrayList<>();
+ for (final DataSchemaNode notKey : notKeys) {
+ newNodes.addAll(argumentHandlerRegistry.getGenericReader(getSchemaContext(), getReadConfigNode()).read(
+ notKey));
+ }
+ return newNodes;
+ }
+
+ @Override
+ protected ConsoleContext getContext(final ListSchemaNode schemaNode) {
+ return new BaseConsoleContext<ListSchemaNode>(schemaNode) {
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.of("[entry]");
+ }
+ };
+ }
+
+}
--- /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.netconf.cli.reader.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class SeparatedNodes {
+ private final Set<DataSchemaNode> keyNodes;
+ private final Set<DataSchemaNode> mandatoryNotKey;
+ private final Set<DataSchemaNode> otherNodes;
+
+ public SeparatedNodes(final Set<DataSchemaNode> keyNodes, final Set<DataSchemaNode> mandatoryNotKey,
+ final Set<DataSchemaNode> otherNodes) {
+ this.keyNodes = keyNodes;
+ this.mandatoryNotKey = mandatoryNotKey;
+ this.otherNodes = otherNodes;
+ }
+
+ public Set<DataSchemaNode> getKeyNodes() {
+ return keyNodes;
+ }
+
+ public Set<DataSchemaNode> getMandatoryNotKey() {
+ return mandatoryNotKey;
+ }
+
+ public Set<DataSchemaNode> getOthers() {
+ return otherNodes;
+ }
+
+ static SeparatedNodes separateNodes(final DataNodeContainer dataNodeContainer) {
+ return separateNodes(dataNodeContainer, false);
+ }
+
+ static SeparatedNodes separateNodes(final DataNodeContainer dataNodeContainer, final boolean removeConfigFalseNodes) {
+ final Set<DataSchemaNode> keys = new HashSet<>();
+ final Set<DataSchemaNode> mandatoryNotKeys = new HashSet<>();
+ final Set<DataSchemaNode> others = new HashSet<>();
+
+ List<QName> keyQNames = Collections.emptyList();
+ if (dataNodeContainer instanceof ListSchemaNode) {
+ keyQNames = ((ListSchemaNode) dataNodeContainer).getKeyDefinition();
+ }
+
+ for (final DataSchemaNode dataSchemaNode : dataNodeContainer.getChildNodes()) {
+ if (removeConfigFalseNodes) {
+ if (!dataSchemaNode.isConfiguration()) {
+ continue;
+ }
+ }
+ if (keyQNames.contains(dataSchemaNode.getQName())) {
+ Preconditions.checkArgument(dataSchemaNode instanceof LeafSchemaNode);
+ keys.add(dataSchemaNode);
+ } else if (dataSchemaNode.getConstraints().isMandatory()) {
+ mandatoryNotKeys.add(dataSchemaNode);
+ } else {
+ others.add(dataSchemaNode);
+ }
+ }
+
+ return new SeparatedNodes(keys, mandatoryNotKeys, others);
+ }
+}
\ No newline at end of file
--- /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.netconf.cli.reader.impl;
+
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.isSkipInput;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import jline.console.completer.AggregateCompleter;
+import jline.console.completer.Completer;
+import jline.console.completer.StringsCompleter;
+import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.io.IOUtil;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class UnionTypeReader {
+ private static final Logger LOG = LoggerFactory.getLogger(UnionTypeReader.class);
+
+ private final ConsoleIO console;
+
+ public UnionTypeReader(final ConsoleIO console) {
+ this.console = console;
+ }
+
+ public Optional<TypeDefinition<?>> read(final TypeDefinition<?> unionTypeDefinition) throws IOException,
+ ReadingException {
+ final ConsoleContext context = getContext(unionTypeDefinition);
+ console.enterContext(context);
+ try {
+ final Map<String, TypeDefinition<?>> mapping = ((UnionConsoleContext) context).getMenuItemMapping();
+ console.formatLn("The element is of type union. Choose concrete type from: %s", mapping.keySet());
+
+ final String rawValue = console.read();
+ if (isSkipInput(rawValue)) {
+ return Optional.absent();
+ }
+ final TypeDefinition<?> value = mapping.get(rawValue);
+ if (value != null) {
+ return Optional.<TypeDefinition<?>> of(value);
+ } else {
+ final String message = String.format("Incorrect type (%s) was specified for union type definition", rawValue);
+ LOG.error(message);
+ throw new ReadingException(message);
+ }
+ } finally {
+ console.leaveContext();
+ }
+ }
+
+ private UnionConsoleContext getContext(final TypeDefinition<?> typeDefinition) {
+ return new UnionConsoleContext(typeDefinition);
+ }
+
+ private class UnionConsoleContext implements ConsoleContext {
+
+ private final TypeDefinition<?> typeDef;
+ private final Map<String, TypeDefinition<?>> menuItemsToTypeDefinitions = new HashMap<>();
+
+ public UnionConsoleContext(final TypeDefinition<?> typeDef) {
+ this.typeDef = typeDef;
+ }
+
+ @Override
+ public Optional<String> getPrompt() {
+ return Optional.of("type[" + typeDef.getQName().getLocalName() + "]");
+ }
+
+ @Override
+ public Completer getCompleter() {
+ List<TypeDefinition<?>> subtypesForMenu = resolveSubtypesFrom(typeDef);
+ if (subtypesForMenu.isEmpty()) {
+ subtypesForMenu = Collections.<TypeDefinition<?>> singletonList(typeDef);
+ }
+ final Collection<String> menuItems = toMenuItem(subtypesForMenu);
+ return new AggregateCompleter(new StringsCompleter(menuItems), new StringsCompleter(IOUtil.SKIP));
+ }
+
+ public Map<String, TypeDefinition<?>> getMenuItemMapping() {
+ return menuItemsToTypeDefinitions;
+ }
+
+ private Collection<String> toMenuItem(final List<TypeDefinition<?>> allTypesBehindUnion) {
+ final List<String> result = new ArrayList<String>();
+ for (final TypeDefinition<?> type : allTypesBehindUnion) {
+ final String menuItem = type.getQName().getLocalName();
+ menuItemsToTypeDefinitions.put(menuItem, type);
+ result.add(menuItem);
+ }
+ return result;
+ }
+
+ /**
+ *
+ * If union type is found in potentialEndTypeCandidate as subtype then
+ * it these subtypes become candidates.
+ *
+ * @param potentialEndTypeCandidate
+ * candidate to node which has no union subtype
+ */
+ private List<TypeDefinition<?>> resolveSubtypesFrom(final TypeDefinition<?> potentialEndTypeCandidate) {
+ if (potentialEndTypeCandidate instanceof UnionTypeDefinition) {
+ return ((UnionTypeDefinition) potentialEndTypeCandidate).getTypes();
+ }
+ if (potentialEndTypeCandidate.getBaseType() == null) {
+ return Collections.emptyList();
+ }
+ return resolveSubtypesFrom(potentialEndTypeCandidate.getBaseType());
+ }
+ }
+}
--- /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.netconf.cli.writer;
+
+public class OutFormatter {
+
+ public static final String INDENT_STEP = " ";
+ public static final String COMPOSITE_OPEN_NODE = " {";
+ public static final String COMPOSITE_CLOSE_NODE = "}";
+ public static final String NEW_LINE = "\n";
+
+ int indentLevel = -1;
+ private String currentIndent = "";
+
+ public OutFormatter indent(final StringBuilder buffer) {
+ buffer.append(currentIndent);
+ return this;
+ }
+
+ public OutFormatter openComposite(final StringBuilder buffer) {
+ buffer.append(COMPOSITE_OPEN_NODE);
+ return this;
+ }
+
+ public OutFormatter closeCompositeWithIndent(final StringBuilder buffer) {
+ buffer.append(currentIndent);
+ buffer.append(COMPOSITE_CLOSE_NODE);
+ return this;
+ }
+
+ public OutFormatter newLine(final StringBuilder buffer) {
+ buffer.append(NEW_LINE);
+ return this;
+ }
+
+ private void prepareIndent() {
+ final StringBuilder output = new StringBuilder();
+ for (int i = 0; i < indentLevel; i++) {
+ output.append(INDENT_STEP);
+ }
+ currentIndent = output.toString();
+ }
+
+ public OutFormatter increaseIndent() {
+ indentLevel++;
+ prepareIndent();
+ return this;
+ }
+
+ public OutFormatter decreaseIndent() {
+ indentLevel--;
+ prepareIndent();
+ return this;
+ }
+
+ public OutFormatter addStringWithIndent(final StringBuilder buffer, final String value) {
+ indent(buffer);
+ buffer.append(value);
+ return this;
+ }
+}
\ No newline at end of file
--- /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.netconf.cli.writer;
+
+public class WriteException extends Exception {
+
+ private static final long serialVersionUID = 8401242676753560336L;
+
+ public WriteException(final String msg, final Exception e) {
+ super(msg, e);
+ }
+
+ public WriteException(final String msg) {
+ super(msg);
+ }
+
+ public static class IncorrectNumberOfNodes extends WriteException {
+ private static final long serialVersionUID = 8910285140705622920L;
+
+ public IncorrectNumberOfNodes(final String msg) {
+ super(msg);
+ }
+ }
+}
--- /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.netconf.cli.writer;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ * Generic handler(writer) of output elements for commands
+ */
+public interface Writer<T extends DataSchemaNode> {
+
+ void write(T dataSchemaNode, List<Node<?>> dataNodes) throws WriteException;
+
+}
--- /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.netconf.cli.writer.custom;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.controller.netconf.cli.writer.WriteException;
+import org.opendaylight.controller.netconf.cli.writer.impl.AbstractWriter;
+import org.opendaylight.controller.netconf.cli.writer.impl.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DataWriter extends AbstractWriter<DataSchemaNode> {
+
+ private final OutFormatter out;
+ private final SchemaContext remoteSchemaContext;
+
+ public DataWriter(final ConsoleIO console, final OutFormatter out, final SchemaContext remoteSchemaContext) {
+ super(console);
+ this.out = out;
+ this.remoteSchemaContext = remoteSchemaContext;
+ }
+
+ @Override
+ protected void writeInner(final DataSchemaNode dataSchemaNode, final List<Node<?>> dataNodes) throws IOException, WriteException {
+ Preconditions.checkArgument(dataNodes.size() == 1, "Expected only 1 element for data node");
+ final Node<?> dataNode = dataNodes.get(0);
+ Preconditions.checkArgument(dataNode instanceof CompositeNode, "Unexpected node type: %s, should be %s", dataNode, CompositeNode.class);
+
+ StringBuilder output = new StringBuilder();
+ out.increaseIndent().addStringWithIndent(output, dataSchemaNode.getQName().getLocalName()).openComposite(output);
+ console.writeLn(output.toString());
+
+ for (final Node<?> childNode : ((CompositeNode) dataNode).getValue()) {
+ final Optional<DataSchemaNode> schemaNode = XmlDocumentUtils.findFirstSchema(childNode.getNodeType(), remoteSchemaContext.getDataDefinitions());
+ Preconditions.checkState(schemaNode.isPresent(), "Unknown data node %s, not defined in schema", childNode.getNodeType());
+ new NormalizedNodeWriter(console, out).write(schemaNode.get(), Collections.<Node<?>>singletonList(childNode));
+ }
+
+ output = new StringBuilder();
+ out.decreaseIndent().closeCompositeWithIndent(output);
+ console.writeLn(output.toString());
+ }
+}
--- /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.netconf.cli.writer.impl;
+
+import java.io.IOException;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.writer.WriteException;
+import org.opendaylight.controller.netconf.cli.writer.Writer;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public abstract class AbstractWriter<T extends DataSchemaNode> implements Writer<T> {
+
+ protected ConsoleIO console;
+
+ public AbstractWriter(final ConsoleIO console) {
+ this.console = console;
+ }
+
+ @Override
+ public void write(final T dataSchemaNode, final List<Node<?>> dataNodes) throws WriteException {
+ try {
+ writeInner(dataSchemaNode, dataNodes);
+ } catch (final IOException e) {
+ throw new WriteException("Unable to write data to output for " + dataSchemaNode.getQName(), e);
+ }
+ }
+
+ protected abstract void writeInner(final T dataSchemaNode, final List<Node<?>> dataNodes) throws IOException,
+ WriteException;
+}
--- /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.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.AugmentationNodeBaseSerializer;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+final class AugmentationNodeCliSerializer extends AugmentationNodeBaseSerializer<String> {
+
+ private final NodeSerializerDispatcher<String> dispatcher;
+ private final OutFormatter out;
+
+ AugmentationNodeCliSerializer(final OutFormatter out, final NodeSerializerDispatcher<String> dispatcher) {
+ this.out = Preconditions.checkNotNull(out);
+ this.dispatcher = Preconditions.checkNotNull(dispatcher);
+ }
+
+ @Override
+ public Iterable<String> serialize(final AugmentationSchema schema, final AugmentationNode node) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, "augment");
+ out.openComposite(output);
+ out.newLine(output);
+
+ for (final String childOutput : super.serialize(schema, node)) {
+ output.append(childOutput);
+ out.newLine(output);
+ }
+
+ out.closeCompositeWithIndent(output);
+ out.decreaseIndent();
+ return Collections.singletonList(output.toString());
+ }
+
+ @Override
+ protected NodeSerializerDispatcher<String> getNodeDispatcher() {
+ return dispatcher;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ChoiceNodeBaseSerializer;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+
+final class ChoiceNodeCliSerializer extends ChoiceNodeBaseSerializer<String> {
+ private final NodeSerializerDispatcher<String> dispatcher;
+ private final OutFormatter out;
+
+ ChoiceNodeCliSerializer(final OutFormatter out, final NodeSerializerDispatcher<String> dispatcher) {
+ this.out = Preconditions.checkNotNull(out);
+ this.dispatcher = Preconditions.checkNotNull(dispatcher);
+ }
+
+ @Override
+ public Iterable<String> serialize(final ChoiceNode schema, final org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode node) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, "choice ");
+ output.append(schema.getQName().getLocalName());
+ output.append(" (");
+ output.append(detectCase(schema, node));
+ output.append(") ");
+ out.openComposite(output);
+ out.newLine(output);
+
+ for (final String childOutput : super.serialize(schema, node)) {
+ output.append(childOutput);
+ out.newLine(output);
+ }
+
+ out.closeCompositeWithIndent(output);
+ out.decreaseIndent();
+ return Collections.singletonList(output.toString());
+ }
+
+ private String detectCase(final ChoiceNode schema, final org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode node) {
+ for (final DataContainerChild<? extends PathArgument, ?> caseChild : node.getValue()) {
+ final QName presentChildQName = caseChild.getNodeType();
+ for (final ChoiceCaseNode choiceCaseNode : schema.getCases()) {
+ if (choiceCaseNode.getDataChildByName(presentChildQName) != null) {
+ // Pick the first case that contains first child node
+ return choiceCaseNode.getQName().getLocalName();
+ }
+ }
+ }
+
+ // Should not happen, nodes should come from one of the cases
+ throw new IllegalStateException("Choice node " + node + " does not conform to choice schema " + schema);
+ }
+
+ @Override
+ protected NodeSerializerDispatcher<String> getNodeDispatcher() {
+ return dispatcher;
+ }
+}
\ No newline at end of file
--- /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.netconf.cli.writer.impl;
+
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+public final class CliOutputFromNormalizedNodeSerializerFactory implements FromNormalizedNodeSerializerFactory<String> {
+ private final ContainerNodeCliSerializer containerSerializer;
+ private final ChoiceNodeCliSerializer choiceSerializer;
+ private final AugmentationNodeCliSerializer augmentSerializer;
+ private final LeafNodeCliSerializer leafNodeSerializer;
+ private final LeafSetNodeCliSerializer leafSetSerializer;
+ private final MapNodeCliSerializer mapNodeSerializer;
+ private final LeafSetEntryNodeCliSerializer leafSetEntryNodeSerializer;
+ private final MapEntryNodeCliSerializer mapEntryNodeSerializer;
+ final NodeSerializerDispatcher<String> dispatcher = new NodeCliSerializerDispatcher(this);
+
+ private CliOutputFromNormalizedNodeSerializerFactory(final OutFormatter out, final XmlCodecProvider codecProvider) {
+
+ containerSerializer = new ContainerNodeCliSerializer(out, dispatcher);
+ choiceSerializer = new ChoiceNodeCliSerializer(out, dispatcher);
+ augmentSerializer = new AugmentationNodeCliSerializer(out, dispatcher);
+ leafNodeSerializer = new LeafNodeCliSerializer(out);
+
+ leafSetEntryNodeSerializer = new LeafSetEntryNodeCliSerializer(out);
+ leafSetSerializer = new LeafSetNodeCliSerializer(out, leafSetEntryNodeSerializer);
+
+ mapEntryNodeSerializer = new MapEntryNodeCliSerializer(out, dispatcher);
+ mapNodeSerializer = new MapNodeCliSerializer(out, mapEntryNodeSerializer);
+ }
+
+ public NodeSerializerDispatcher<String> getDispatcher() {
+ return dispatcher;
+ }
+
+ public static CliOutputFromNormalizedNodeSerializerFactory getInstance(final OutFormatter out,
+ final XmlCodecProvider codecProvider) {
+ return new CliOutputFromNormalizedNodeSerializerFactory(out, codecProvider);
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, AugmentationNode, AugmentationSchema> getAugmentationNodeSerializer() {
+ return augmentSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, ChoiceNode, org.opendaylight.yangtools.yang.model.api.ChoiceNode> getChoiceNodeSerializer() {
+ return choiceSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, ContainerNode, ContainerSchemaNode> getContainerNodeSerializer() {
+ return containerSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, LeafNode<?>, LeafSchemaNode> getLeafNodeSerializer() {
+ return leafNodeSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, LeafSetEntryNode<?>, LeafListSchemaNode> getLeafSetEntryNodeSerializer() {
+ return leafSetEntryNodeSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, LeafSetNode<?>, LeafListSchemaNode> getLeafSetNodeSerializer() {
+ return leafSetSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, MapEntryNode, ListSchemaNode> getMapEntryNodeSerializer() {
+ return mapEntryNodeSerializer;
+ }
+
+ @Override
+ public FromNormalizedNodeSerializer<String, MapNode, ListSchemaNode> getMapNodeSerializer() {
+ return mapNodeSerializer;
+ }
+
+}
\ No newline at end of file
--- /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.netconf.cli.writer.impl;
+
+import java.io.IOException;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.controller.netconf.cli.writer.WriteException;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class CompositeNodeWriter extends AbstractWriter<DataSchemaNode> {
+
+ private final OutFormatter outFormatter;
+
+ public CompositeNodeWriter(final ConsoleIO console, final OutFormatter outFormatter) {
+ super(console);
+ this.outFormatter = outFormatter;
+ }
+
+ @Override
+ protected void writeInner(final DataSchemaNode dataSchemaNode, final List<Node<?>> dataNodes) throws IOException, WriteException {
+ final StringBuilder output = new StringBuilder();
+ writeNode(dataNodes, output);
+ console.writeLn(output);
+ }
+
+ private void writeNode(final List<Node<?>> dataNodes, final StringBuilder output) throws IOException, WriteException {
+ for (final Node<?> dataNode : dataNodes) {
+ outFormatter.increaseIndent();
+ outFormatter.addStringWithIndent(output, dataNode.getNodeType().getLocalName());
+ if (dataNode instanceof CompositeNode) {
+ outFormatter.openComposite(output);
+ outFormatter.newLine(output);
+ writeNode(((CompositeNode) dataNode).getValue(), output);
+ outFormatter.closeCompositeWithIndent(output);
+ outFormatter.newLine(output);
+ } else if (dataNode instanceof SimpleNode<?>) {
+ final SimpleNode<?> simpleNode = (SimpleNode<?>) dataNode;
+ output.append(" ");
+ output.append(simpleNode.getValue());
+ outFormatter.newLine(output);
+ }
+ outFormatter.decreaseIndent();
+ }
+ }
+}
--- /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.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ContainerNodeBaseSerializer;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+final class ContainerNodeCliSerializer extends ContainerNodeBaseSerializer<String> {
+
+ private final NodeSerializerDispatcher<String> dispatcher;
+ private final OutFormatter out;
+
+ ContainerNodeCliSerializer(final OutFormatter out, final NodeSerializerDispatcher<String> dispatcher) {
+ this.out = Preconditions.checkNotNull(out);
+ this.dispatcher = Preconditions.checkNotNull(dispatcher);
+ }
+
+ @Override
+ public Iterable<String> serialize(final ContainerSchemaNode schema, final ContainerNode containerNode) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, containerNode.getNodeType().getLocalName());
+ out.openComposite(output);
+ out.newLine(output);
+
+ for (final String childOutput : super.serialize(schema, containerNode)) {
+ output.append(childOutput);
+ out.newLine(output);
+ }
+
+ out.closeCompositeWithIndent(output);
+ out.decreaseIndent();
+ return Collections.singletonList(output.toString());
+ }
+
+ @Override
+ protected NodeSerializerDispatcher<String> getNodeDispatcher() {
+ return dispatcher;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.LeafNodeBaseSerializer;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+final class LeafNodeCliSerializer extends LeafNodeBaseSerializer<String> {
+ private final OutFormatter out;
+
+ LeafNodeCliSerializer(final OutFormatter out) {
+ this.out = Preconditions.checkNotNull(out);
+ }
+
+ @Override
+ public String serializeLeaf(final LeafSchemaNode schema, final LeafNode<?> node) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, node.getNodeType().getLocalName());
+ output.append(" ");
+ output.append(node.getValue());
+ out.decreaseIndent();
+ return output.toString();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.cli.writer.impl;
+
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.LeafSetEntryNodeBaseSerializer;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+final class LeafSetEntryNodeCliSerializer extends LeafSetEntryNodeBaseSerializer<String> {
+
+ private final OutFormatter out;
+
+ public LeafSetEntryNodeCliSerializer(final OutFormatter out) {
+ this.out = out;
+ }
+
+ @Override
+ protected String serializeLeaf(final LeafListSchemaNode schema, final LeafSetEntryNode<?> node) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, node.getValue().toString());
+ out.decreaseIndent();
+ return output.toString();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.cli.writer.impl;
+
+import java.util.Collections;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+final class LeafSetNodeCliSerializer implements
+ FromNormalizedNodeSerializer<String, LeafSetNode<?>, LeafListSchemaNode> {
+ private final LeafSetEntryNodeCliSerializer leafSetEntryNodeSerializer;
+ private final OutFormatter out;
+
+ LeafSetNodeCliSerializer(final OutFormatter out, final LeafSetEntryNodeCliSerializer leafSetEntryNodeSerializer) {
+ this.out = out;
+ this.leafSetEntryNodeSerializer = leafSetEntryNodeSerializer;
+ }
+
+ @Override
+ public Iterable<String> serialize(final LeafListSchemaNode schema, final LeafSetNode<?> node) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, node.getNodeType().getLocalName());
+ out.openComposite(output);
+ out.newLine(output);
+ for (final LeafSetEntryNode<?> leafEntryNode : node.getValue()) {
+ final Iterable<String> valueFromLeafSetEntry = leafSetEntryNodeSerializer.serialize(schema, leafEntryNode);
+ output.append(valueFromLeafSetEntry.iterator().next());
+ out.newLine(output);
+ }
+ out.closeCompositeWithIndent(output);
+ out.decreaseIndent();
+ return Collections.singletonList(output.toString());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.MapEntryNodeBaseSerializer;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class MapEntryNodeCliSerializer extends MapEntryNodeBaseSerializer<String> {
+
+ private final NodeSerializerDispatcher<String> dispatcher;
+ private final OutFormatter out;
+
+ MapEntryNodeCliSerializer(final OutFormatter out, final NodeSerializerDispatcher<String> dispatcher) {
+ this.out = Preconditions.checkNotNull(out);
+ this.dispatcher = Preconditions.checkNotNull(dispatcher);
+ }
+
+ @Override
+ public Iterable<String> serialize(final ListSchemaNode schema, final MapEntryNode node) {
+ final StringBuilder output = new StringBuilder();
+ out.increaseIndent();
+ out.addStringWithIndent(output, node.getNodeType().getLocalName());
+ serializeKeysIfPresent(node, output);
+
+ out.openComposite(output);
+ out.newLine(output);
+
+ for (final String childOutput : super.serialize(schema, node)) {
+ output.append(childOutput);
+ out.newLine(output);
+ }
+
+ out.closeCompositeWithIndent(output);
+ out.newLine(output);
+ out.decreaseIndent();
+ return Collections.singletonList(output.toString());
+ }
+
+ private void serializeKeysIfPresent(final MapEntryNode node, final StringBuilder output) {
+ final Map<QName, Object> keyValues = node.getIdentifier().getKeyValues();
+ if (keyValues.isEmpty()) {
+ return;
+ }
+
+ int i = 0;
+ output.append(" [");
+ for (final Entry<QName, Object> qNameObjectEntry : keyValues.entrySet()) {
+ output.append(qNameObjectEntry.getKey().getLocalName());
+ output.append("=");
+ output.append(qNameObjectEntry.getValue().toString());
+ if (++i != keyValues.size()) {
+ output.append(", ");
+ }
+ }
+ output.append("]");
+ }
+
+ @Override
+ protected NodeSerializerDispatcher<String> getNodeDispatcher() {
+ return dispatcher;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class MapNodeCliSerializer implements FromNormalizedNodeSerializer<String, MapNode, ListSchemaNode> {
+
+ private final FromNormalizedNodeSerializer<String, MapEntryNode, ListSchemaNode> mapEntrySerializer;
+ private final OutFormatter out;
+
+ MapNodeCliSerializer(final OutFormatter out, final MapEntryNodeCliSerializer mapEntrySerializer) {
+ this.out = Preconditions.checkNotNull(out);
+ this.mapEntrySerializer = mapEntrySerializer;
+ }
+
+ @Override
+ public Iterable<String> serialize(final ListSchemaNode schema, final MapNode node) {
+ final StringBuilder output = new StringBuilder();
+
+ out.increaseIndent();
+ out.addStringWithIndent(output, node.getNodeType().getLocalName());
+ output.append(" ");
+ out.openComposite(output);
+ out.newLine(output);
+
+ for (final MapEntryNode mapEntryNode : node.getValue()) {
+ final Iterable<String> valueFromLeafSetEntry = mapEntrySerializer.serialize(schema, mapEntryNode);
+ output.append(valueFromLeafSetEntry.iterator().next());
+ }
+
+ out.closeCompositeWithIndent(output);
+ out.decreaseIndent();
+
+ return Collections.singletonList(output.toString());
+ }
+}
--- /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.cli.writer.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+public class NodeCliSerializerDispatcher implements NodeSerializerDispatcher<String> {
+ private final FromNormalizedNodeSerializerFactory<String> factory;
+
+ public NodeCliSerializerDispatcher(final FromNormalizedNodeSerializerFactory<String> factory) {
+ this.factory = Preconditions.checkNotNull(factory);
+ }
+
+ @Override
+ public final Iterable<String> dispatchChildElement(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ if (dataContainerChild instanceof ContainerNode) {
+ return onContainerNode(childSchema, dataContainerChild);
+ } else if (dataContainerChild instanceof LeafNode<?>) {
+ return onLeafNode(childSchema, dataContainerChild);
+ } else if (dataContainerChild instanceof MixinNode) {
+ if (dataContainerChild instanceof LeafSetNode<?>) {
+ return onLeafListNode(childSchema, dataContainerChild);
+ } else if (dataContainerChild instanceof MapNode) {
+ return onListNode(childSchema, dataContainerChild);
+ } else if (dataContainerChild instanceof ChoiceNode) {
+ return onChoiceNode(childSchema, dataContainerChild);
+ } else if (dataContainerChild instanceof AugmentationNode) {
+ return onAugmentationSchema(childSchema, dataContainerChild);
+ }
+ }
+ throw new IllegalArgumentException("Unable to serialize " + childSchema);
+ }
+
+ private Iterable<String> onAugmentationSchema(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ checkSchemaCompatibility(childSchema, AugmentationSchema.class, dataContainerChild);
+ return factory.getAugmentationNodeSerializer().serialize((AugmentationSchema) childSchema,
+ (AugmentationNode) dataContainerChild);
+ }
+
+ private Iterable<String> onChoiceNode(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ checkSchemaCompatibility(childSchema, org.opendaylight.yangtools.yang.model.api.ChoiceNode.class,
+ dataContainerChild);
+ return factory.getChoiceNodeSerializer().serialize(
+ (org.opendaylight.yangtools.yang.model.api.ChoiceNode) childSchema, (ChoiceNode) dataContainerChild);
+ }
+
+ private Iterable<String> onListNode(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ checkSchemaCompatibility(childSchema, ListSchemaNode.class, dataContainerChild);
+ return factory.getMapNodeSerializer().serialize((ListSchemaNode) childSchema, (MapNode) dataContainerChild);
+ }
+
+ private Iterable<String> onLeafListNode(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ checkSchemaCompatibility(childSchema, LeafListSchemaNode.class, dataContainerChild);
+ return factory.getLeafSetNodeSerializer().serialize((LeafListSchemaNode) childSchema,
+ (LeafSetNode<?>) dataContainerChild);
+ }
+
+ private Iterable<String> onLeafNode(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ checkSchemaCompatibility(childSchema, LeafSchemaNode.class, dataContainerChild);
+ final Iterable<String> elements = factory.getLeafNodeSerializer().serialize((LeafSchemaNode) childSchema,
+ (LeafNode<?>) dataContainerChild);
+ checkOnlyOneSerializedElement(elements, dataContainerChild);
+ return elements;
+ }
+
+ private static void checkOnlyOneSerializedElement(final Iterable<?> elements,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ final int size = Iterables.size(elements);
+ Preconditions.checkArgument(size == 1,
+ "Unexpected count of elements for entry serialized from: %s, should be 1, was: %s", dataContainerChild,
+ size);
+ }
+
+ private Iterable<String> onContainerNode(final Object childSchema,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ checkSchemaCompatibility(childSchema, ContainerSchemaNode.class, dataContainerChild);
+
+ final Iterable<String> elements = factory.getContainerNodeSerializer().serialize(
+ (ContainerSchemaNode) childSchema, (ContainerNode) dataContainerChild);
+ checkOnlyOneSerializedElement(elements, dataContainerChild);
+ return elements;
+ }
+
+ private static void checkSchemaCompatibility(final Object childSchema, final Class<?> containerSchemaNodeClass,
+ final DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> dataContainerChild) {
+ Preconditions.checkArgument(containerSchemaNodeClass.isAssignableFrom(childSchema.getClass()),
+ "Incompatible schema: %s with node: %s, expected: %s", childSchema, dataContainerChild,
+ containerSchemaNodeClass);
+ }
+}
\ No newline at end of file
--- /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.netconf.cli.writer.impl;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.controller.netconf.cli.writer.WriteException;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.data.json.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NormalizedNodeWriter extends AbstractWriter<DataSchemaNode> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeWriter.class);
+ private final OutFormatter out;
+
+ public NormalizedNodeWriter(final ConsoleIO console, final OutFormatter out) {
+ super(console);
+ this.out = out;
+ }
+
+ public void writeInner(final DataSchemaNode dataSchemaNode, final List<Node<?>> dataNodes) throws WriteException,
+ IOException {
+
+ // TODO - add getDispatcher method to CnSnToNormalizedNodeParserFactory
+ // to be able call dispatchChildElement
+ final DataContainerChild<? extends PathArgument, ?> dataContainerChild = parseToNormalizedNode(dataNodes,
+ dataSchemaNode);
+
+ if (dataContainerChild != null) {
+ console.writeLn(serializeToCliOutput(dataContainerChild, dataSchemaNode));
+ }
+
+ }
+
+ private String serializeToCliOutput(final DataContainerChild<? extends PathArgument, ?> dataContainerChild,
+ final DataSchemaNode childSchema) {
+ final CliOutputFromNormalizedNodeSerializerFactory factorySerialization = CliOutputFromNormalizedNodeSerializerFactory
+ .getInstance(out, DomUtils.defaultValueCodecProvider());
+ final NodeSerializerDispatcher<String> dispatcher = factorySerialization.getDispatcher();
+ final Iterable<String> result = dispatcher.dispatchChildElement(childSchema, dataContainerChild);
+
+ if (result == null) {
+ return "";
+ }
+
+ final Iterator<String> output = result.iterator();
+ if (!output.hasNext()) {
+ return "";
+ }
+
+ return output.next();
+ }
+
+ private DataContainerChild<? extends PathArgument, ?> parseToNormalizedNode(final List<Node<?>> dataNodes,
+ final DataSchemaNode dataSchemaNode) {
+ final CnSnToNormalizedNodeParserFactory factoryParsing = CnSnToNormalizedNodeParserFactory.getInstance();
+ if (dataSchemaNode instanceof ContainerSchemaNode) {
+ return factoryParsing.getContainerNodeParser().parse(dataNodes, (ContainerSchemaNode) dataSchemaNode);
+ } else if (dataSchemaNode instanceof LeafSchemaNode) {
+ return factoryParsing.getLeafNodeParser().parse(dataNodes, (LeafSchemaNode) dataSchemaNode);
+ } else if (dataSchemaNode instanceof LeafListSchemaNode) {
+ return factoryParsing.getLeafSetNodeParser().parse(dataNodes, (LeafListSchemaNode) dataSchemaNode);
+ } else if (dataSchemaNode instanceof ListSchemaNode) {
+ return factoryParsing.getMapNodeParser().parse(dataNodes, (ListSchemaNode) dataSchemaNode);
+ } else if (dataSchemaNode instanceof ChoiceNode) {
+ return factoryParsing.getChoiceNodeParser().parse(dataNodes, (ChoiceNode) dataSchemaNode);
+ } else if (dataSchemaNode instanceof AugmentationSchema) {
+ return factoryParsing.getAugmentationNodeParser().parse(dataNodes, (AugmentationSchema) dataSchemaNode);
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+
+<configuration scan="true">
+ <appender name="netconfcli.log" class="ch.qos.logback.core.FileAppender">
+ <file>netconfcli.log</file>
+
+ <encoder>
+ <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level
+ %logger{35} - %msg%n</pattern>
+ </encoder>
+
+ </appender>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder
+ by default -->
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+
+
+ <root level="trace">
+ <appender-ref ref="netconfcli.log" />
+ <!-- <appender-ref ref="STDOUT" /> -->
+ </root>
+
+ <!-- <logger name="org.opendaylight.yangtools.yang.model.api" level="TRACE"
+ additivity="false"> <appender-ref ref="netconfcli.log" /> </logger> -->
+</configuration>
--- /dev/null
+module ietf-inet-types {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+ prefix "inet";
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: David Partain
+ <mailto:david.partain@ericsson.com>
+
+ WG Chair: David Kessens
+ <mailto:david.kessens@nsn.com>
+
+ Editor: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>";
+
+ description
+ "This module contains a collection of generally useful derived
+ YANG data types for Internet addresses and related things.
+
+ Copyright (c) 2010 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+
+
+ Redistribution and use in source and binary forms, with or without
+ modification, is permitted pursuant to, and subject to the license
+ terms contained in, the Simplified BSD License set forth in Section
+ 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6021; see
+ the RFC itself for full legal notices.";
+
+ revision 2010-09-24 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6021: Common YANG Data Types";
+ }
+
+ /*** collection of protocol field related types ***/
+
+ typedef ip-version {
+ type enumeration {
+ enum unknown {
+ value "0";
+ description
+ "An unknown or unspecified version of the Internet protocol.";
+ }
+ enum ipv4 {
+ value "1";
+ description
+ "The IPv4 protocol as defined in RFC 791.";
+ }
+ enum ipv6 {
+ value "2";
+ description
+ "The IPv6 protocol as defined in RFC 2460.";
+ }
+ }
+ description
+ "This value represents the version of the IP protocol.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetVersion textual convention of the SMIv2.";
+ reference
+ "RFC 791: Internet Protocol
+ RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ typedef dscp {
+ type uint8 {
+ range "0..63";
+ }
+ description
+ "The dscp type represents a Differentiated Services Code-Point
+ that may be used for marking packets in a traffic stream.
+
+ In the value set and its semantics, this type is equivalent
+ to the Dscp textual convention of the SMIv2.";
+ reference
+ "RFC 3289: Management Information Base for the Differentiated
+ Services Architecture
+ RFC 2474: Definition of the Differentiated Services Field
+ (DS Field) in the IPv4 and IPv6 Headers
+ RFC 2780: IANA Allocation Guidelines For Values In
+ the Internet Protocol and Related Headers";
+ }
+
+ typedef ipv6-flow-label {
+ type uint32 {
+ range "0..1048575";
+ }
+ description
+ "The flow-label type represents flow identifier or Flow Label
+ in an IPv6 packet header that may be used to discriminate
+ traffic flows.
+
+ In the value set and its semantics, this type is equivalent
+ to the IPv6FlowLabel textual convention of the SMIv2.";
+ reference
+ "RFC 3595: Textual Conventions for IPv6 Flow Label
+ RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+ }
+
+ typedef port-number {
+ type uint16 {
+ range "0..65535";
+ }
+ description
+ "The port-number type represents a 16-bit port number of an
+ Internet transport layer protocol such as UDP, TCP, DCCP, or
+ SCTP. Port numbers are assigned by IANA. A current list of
+ all assignments is available from <http://www.iana.org/>.
+
+ Note that the port number value zero is reserved by IANA. In
+ situations where the value zero does not make sense, it can
+ be excluded by subtyping the port-number type.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetPortNumber textual convention of the SMIv2.";
+ reference
+ "RFC 768: User Datagram Protocol
+ RFC 793: Transmission Control Protocol
+ RFC 4960: Stream Control Transmission Protocol
+ RFC 4340: Datagram Congestion Control Protocol (DCCP)
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ /*** collection of autonomous system related types ***/
+
+ typedef as-number {
+ type uint32;
+ description
+ "The as-number type represents autonomous system numbers
+ which identify an Autonomous System (AS). An AS is a set
+ of routers under a single technical administration, using
+ an interior gateway protocol and common metrics to route
+ packets within the AS, and using an exterior gateway
+ protocol to route packets to other ASs'. IANA maintains
+ the AS number space and has delegated large parts to the
+ regional registries.
+
+ Autonomous system numbers were originally limited to 16
+ bits. BGP extensions have enlarged the autonomous system
+ number space to 32 bits. This type therefore uses an uint32
+ base type without a range restriction in order to support
+ a larger autonomous system number space.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetAutonomousSystemNumber textual convention of
+ the SMIv2.";
+ reference
+ "RFC 1930: Guidelines for creation, selection, and registration
+ of an Autonomous System (AS)
+ RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+ RFC 4893: BGP Support for Four-octet AS Number Space
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ /*** collection of IP address and hostname related types ***/
+
+ typedef ip-address {
+ type union {
+ type inet:ipv4-address;
+ type inet:ipv6-address;
+ }
+ description
+ "The ip-address type represents an IP address and is IP
+ version neutral. The format of the textual representations
+ implies the IP version.";
+ }
+
+ typedef ipv4-address {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '(%[\p{N}\p{L}]+)?';
+ }
+ description
+ "The ipv4-address type represents an IPv4 address in
+ dotted-quad notation. The IPv4 address may include a zone
+ index, separated by a % sign.
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format for the zone index is the numerical
+ format";
+ }
+
+ typedef ipv6-address {
+ type string {
+ pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ + '(%[\p{N}\p{L}]+)?';
+ pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ + '(%.+)?';
+ }
+ description
+ "The ipv6-address type represents an IPv6 address in full,
+ mixed, shortened, and shortened-mixed notation. The IPv6
+ address may include a zone index, separated by a % sign.
+
+
+
+
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format of IPv6 addresses uses the compressed
+ format described in RFC 4291, Section 2.2, item 2 with the
+ following additional rules: the :: substitution must be
+ applied to the longest sequence of all-zero 16-bit chunks
+ in an IPv6 address. If there is a tie, the first sequence
+ of all-zero 16-bit chunks is replaced by ::. Single
+ all-zero 16-bit chunks are not compressed. The canonical
+ format uses lowercase characters and leading zeros are
+ not allowed. The canonical format for the zone index is
+ the numerical format as described in RFC 4007, Section
+ 11.2.";
+ reference
+ "RFC 4291: IP Version 6 Addressing Architecture
+ RFC 4007: IPv6 Scoped Address Architecture
+ RFC 5952: A Recommendation for IPv6 Address Text Representation";
+ }
+
+ typedef ip-prefix {
+ type union {
+ type inet:ipv4-prefix;
+ type inet:ipv6-prefix;
+ }
+ description
+ "The ip-prefix type represents an IP prefix and is IP
+ version neutral. The format of the textual representations
+ implies the IP version.";
+ }
+
+ typedef ipv4-prefix {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+ }
+ description
+ "The ipv4-prefix type represents an IPv4 address prefix.
+ The prefix length is given by the number following the
+ slash character and must be less than or equal to 32.
+
+
+
+ A prefix length value of n corresponds to an IP address
+ mask that has n contiguous 1-bits from the most
+ significant bit (MSB) and all other bits set to 0.
+
+ The canonical format of an IPv4 prefix has all bits of
+ the IPv4 address set to zero that are not part of the
+ IPv4 prefix.";
+ }
+
+ typedef ipv6-prefix {
+ type string {
+ pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+ pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ + '(/.+)';
+ }
+ description
+ "The ipv6-prefix type represents an IPv6 address prefix.
+ The prefix length is given by the number following the
+ slash character and must be less than or equal 128.
+
+ A prefix length value of n corresponds to an IP address
+ mask that has n contiguous 1-bits from the most
+ significant bit (MSB) and all other bits set to 0.
+
+ The IPv6 address should have all bits that do not belong
+ to the prefix set to zero.
+
+ The canonical format of an IPv6 prefix has all bits of
+ the IPv6 address set to zero that are not part of the
+ IPv6 prefix. Furthermore, IPv6 address is represented
+ in the compressed format described in RFC 4291, Section
+ 2.2, item 2 with the following additional rules: the ::
+ substitution must be applied to the longest sequence of
+ all-zero 16-bit chunks in an IPv6 address. If there is
+ a tie, the first sequence of all-zero 16-bit chunks is
+ replaced by ::. Single all-zero 16-bit chunks are not
+ compressed. The canonical format uses lowercase
+ characters and leading zeros are not allowed.";
+ reference
+ "RFC 4291: IP Version 6 Addressing Architecture";
+ }
+
+
+ /*** collection of domain name and URI types ***/
+
+ typedef domain-name {
+ type string {
+ pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+ + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+ + '|\.';
+ length "1..253";
+ }
+ description
+ "The domain-name type represents a DNS domain name. The
+ name SHOULD be fully qualified whenever possible.
+
+ Internet domain names are only loosely specified. Section
+ 3.5 of RFC 1034 recommends a syntax (modified in Section
+ 2.1 of RFC 1123). The pattern above is intended to allow
+ for current practice in domain name use, and some possible
+ future expansion. It is designed to hold various types of
+ domain names, including names used for A or AAAA records
+ (host names) and other records, such as SRV records. Note
+ that Internet host names have a stricter syntax (described
+ in RFC 952) than the DNS recommendations in RFCs 1034 and
+ 1123, and that systems that want to store host names in
+ schema nodes using the domain-name type are recommended to
+ adhere to this stricter standard to ensure interoperability.
+
+ The encoding of DNS names in the DNS protocol is limited
+ to 255 characters. Since the encoding consists of labels
+ prefixed by a length bytes and there is a trailing NULL
+ byte, only 253 characters can appear in the textual dotted
+ notation.
+
+ The description clause of schema nodes using the domain-name
+ type MUST describe when and how these names are resolved to
+ IP addresses. Note that the resolution of a domain-name value
+ may require to query multiple DNS records (e.g., A for IPv4
+ and AAAA for IPv6). The order of the resolution process and
+ which DNS record takes precedence can either be defined
+ explicitely or it may depend on the configuration of the
+ resolver.
+
+ Domain-name values use the US-ASCII encoding. Their canonical
+ format uses lowercase US-ASCII characters. Internationalized
+ domain names MUST be encoded in punycode as described in RFC
+ 3492";
+ reference
+ "RFC 952: DoD Internet Host Table Specification
+ RFC 1034: Domain Names - Concepts and Facilities
+ RFC 1123: Requirements for Internet Hosts -- Application
+ and Support
+ RFC 2782: A DNS RR for specifying the location of services
+ (DNS SRV)
+ RFC 3492: Punycode: A Bootstring encoding of Unicode for
+ Internationalized Domain Names in Applications
+ (IDNA)
+ RFC 5891: Internationalizing Domain Names in Applications
+ (IDNA): Protocol";
+ }
+
+ typedef host {
+ type union {
+ type inet:ip-address;
+ type inet:domain-name;
+ }
+ description
+ "The host type represents either an IP address or a DNS
+ domain name.";
+ }
+
+ typedef uri {
+ type string;
+ description
+ "The uri type represents a Uniform Resource Identifier
+ (URI) as defined by STD 66.
+
+ Objects using the uri type MUST be in US-ASCII encoding,
+ and MUST be normalized as described by RFC 3986 Sections
+ 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary
+ percent-encoding is removed, and all case-insensitive
+ characters are set to lowercase except for hexadecimal
+ digits, which are normalized to uppercase as described in
+ Section 6.2.2.1.
+
+ The purpose of this normalization is to help provide
+ unique URIs. Note that this normalization is not
+ sufficient to provide uniqueness. Two URIs that are
+ textually distinct after this normalization may still be
+ equivalent.
+
+ Objects using the uri type may restrict the schemes that
+ they permit. For example, 'data:' and 'urn:' schemes
+ might not be appropriate.
+
+ A zero-length URI is not a valid URI. This can be used to
+ express 'URI absent' where required.
+
+ In the value set and its semantics, this type is equivalent
+ to the Uri SMIv2 textual convention defined in RFC 5017.";
+ reference
+ "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+ RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+ Group: Uniform Resource Identifiers (URIs), URLs,
+ and Uniform Resource Names (URNs): Clarifications
+ and Recommendations
+ RFC 5017: MIB Textual Conventions for Uniform Resource
+ Identifiers (URIs)";
+ }
+
+}
--- /dev/null
+module netconf-cli-ext {
+
+ namespace "urn:ietf:params:xml:ns:netconf:base:1.0:cli";
+
+ prefix cliext;
+
+ revision 2014-05-26 {
+ description
+ "Initial revision";
+ }
+
+ extension argument-handler {
+ description
+ "Links custom argument reader to an input argument";
+ argument "name";
+ }
+
+}
--- /dev/null
+module netconf-cli {
+
+ namespace "netconf:cli";
+ prefix ncli;
+
+ import ietf-inet-types { prefix inet; revision-date 2010-09-24; }
+ import netconf-cli-ext { prefix cliext; revision-date 2014-05-26; }
+
+
+ revision 2014-05-22 {
+ description
+ "Initial revision.";
+ }
+
+ extension java-class {
+ description
+ "This could be used to link between rpc yang definition and custom command implementation";
+
+ argument "name";
+ }
+
+ rpc help {
+ description
+ "Display help";
+
+ output {
+ list commands {
+
+ key "id";
+ leaf id {
+ type string;
+ }
+ leaf description {
+ type string;
+ }
+ }
+ }
+ }
+
+ rpc close {
+ description
+ "Close the whole cli";
+ }
+
+ rpc connect {
+
+ description
+ "Connect to a remote netconf device, if not connected yet. Connection initialization is blocking and might take some time, depending on amount of yang schemas in remote device.";
+
+ input {
+
+ // TODO yangtools keep input arguments unordered so the ordering in cli is random
+ leaf address-name {
+ type inet:host;
+ default localhost;
+ }
+
+ leaf address-port {
+ type inet:port-number;
+ default 830;
+ }
+
+ leaf user-name {
+ type string;
+ }
+
+ leaf user-password {
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.reader.custom.PasswordReader;
+ type string;
+ }
+ }
+
+ output {
+ leaf status {
+ type string;
+ }
+
+ leaf-list remote-commands {
+ type string;
+ }
+ }
+ }
+
+
+ rpc disconnect {
+
+ description
+ "Disconnect from a netconf device that is currently connected";
+
+ output {
+ leaf status {
+ type string;
+ }
+ }
+ }
+
+}
--- /dev/null
+module ietf-netconf {
+
+ // the namespace for NETCONF XML definitions is unchanged
+ // from RFC 4741, which this document replaces
+ namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+ prefix nc;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import netconf-cli-ext { prefix cliext; revision-date 2014-05-26; }
+
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Martin Bjorklund
+ <mbj@tail-f.com>
+
+ Editor: Juergen Schoenwaelder
+ <j.schoenwaelder@jacobs-university.de>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>";
+ description
+ "NETCONF Protocol Data Types and Protocol Operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6241; see
+ the RFC itself for full legal notices.";
+ revision 2011-06-01 {
+ description
+ "Initial revision";
+ reference
+ "RFC 6241: Network Configuration Protocol";
+ }
+
+ extension get-filter-element-attributes {
+ description
+ "If this extension is present within an 'anyxml'
+ statement named 'filter', which must be conceptually
+ defined within the RPC input section for the <get>
+ and <get-config> protocol operations, then the
+ following unqualified XML attribute is supported
+ within the <filter> element, within a <get> or
+ <get-config> protocol operation:
+
+ type : optional attribute with allowed
+ value strings 'subtree' and 'xpath'.
+ If missing, the default value is 'subtree'.
+
+ If the 'xpath' feature is supported, then the
+ following unqualified XML attribute is
+ also supported:
+
+ select: optional attribute containing a
+ string representing an XPath expression.
+ The 'type' attribute must be equal to 'xpath'
+ if this attribute is present.";
+ }
+
+ // NETCONF capabilities defined as features
+ feature writable-running {
+ description
+ "NETCONF :writable-running capability;
+ If the server advertises the :writable-running
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.2";
+ }
+
+ feature candidate {
+ description
+ "NETCONF :candidate capability;
+ If the server advertises the :candidate
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.3";
+ }
+
+ feature confirmed-commit {
+ if-feature candidate;
+ description
+ "NETCONF :confirmed-commit:1.1 capability;
+ If the server advertises the :confirmed-commit:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+
+ reference "RFC 6241, Section 8.4";
+ }
+
+ feature rollback-on-error {
+ description
+ "NETCONF :rollback-on-error capability;
+ If the server advertises the :rollback-on-error
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.5";
+ }
+
+ feature validate {
+ description
+ "NETCONF :validate:1.1 capability;
+ If the server advertises the :validate:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.6";
+ }
+
+ feature startup {
+ description
+ "NETCONF :startup capability;
+ If the server advertises the :startup
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.7";
+ }
+
+ feature url {
+ description
+ "NETCONF :url capability;
+ If the server advertises the :url
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.8";
+ }
+
+ feature xpath {
+ description
+ "NETCONF :xpath capability;
+ If the server advertises the :xpath
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.9";
+ }
+
+ // NETCONF Simple Types
+
+ typedef session-id-type {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "NETCONF Session Id";
+ }
+
+ typedef session-id-or-zero-type {
+ type uint32;
+ description
+ "NETCONF Session Id or Zero to indicate none";
+ }
+ typedef error-tag-type {
+ type enumeration {
+ enum in-use {
+ description
+ "The request requires a resource that
+ already is in use.";
+ }
+ enum invalid-value {
+ description
+ "The request specifies an unacceptable value for one
+ or more parameters.";
+ }
+ enum too-big {
+ description
+ "The request or response (that would be generated) is
+ too large for the implementation to handle.";
+ }
+ enum missing-attribute {
+ description
+ "An expected attribute is missing.";
+ }
+ enum bad-attribute {
+ description
+ "An attribute value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-attribute {
+ description
+ "An unexpected attribute is present.";
+ }
+ enum missing-element {
+ description
+ "An expected element is missing.";
+ }
+ enum bad-element {
+ description
+ "An element value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-element {
+ description
+ "An unexpected element is present.";
+ }
+ enum unknown-namespace {
+ description
+ "An unexpected namespace is present.";
+ }
+ enum access-denied {
+ description
+ "Access to the requested protocol operation or
+ data model is denied because authorization failed.";
+ }
+ enum lock-denied {
+ description
+ "Access to the requested lock is denied because the
+ lock is currently held by another entity.";
+ }
+ enum resource-denied {
+ description
+ "Request could not be completed because of
+ insufficient resources.";
+ }
+ enum rollback-failed {
+ description
+ "Request to roll back some configuration change (via
+ rollback-on-error or <discard-changes> operations)
+ was not completed for some reason.";
+
+ }
+ enum data-exists {
+ description
+ "Request could not be completed because the relevant
+ data model content already exists. For example,
+ a 'create' operation was attempted on data that
+ already exists.";
+ }
+ enum data-missing {
+ description
+ "Request could not be completed because the relevant
+ data model content does not exist. For example,
+ a 'delete' operation was attempted on
+ data that does not exist.";
+ }
+ enum operation-not-supported {
+ description
+ "Request could not be completed because the requested
+ operation is not supported by this implementation.";
+ }
+ enum operation-failed {
+ description
+ "Request could not be completed because the requested
+ operation failed for some reason not covered by
+ any other error condition.";
+ }
+ enum partial-operation {
+ description
+ "This error-tag is obsolete, and SHOULD NOT be sent
+ by servers conforming to this document.";
+ }
+ enum malformed-message {
+ description
+ "A message could not be handled because it failed to
+ be parsed correctly. For example, the message is not
+ well-formed XML or it uses an invalid character set.";
+ }
+ }
+ description "NETCONF Error Tag";
+ reference "RFC 6241, Appendix A";
+ }
+
+ typedef error-severity-type {
+ type enumeration {
+ enum error {
+ description "Error severity";
+ }
+ enum warning {
+ description "Warning severity";
+ }
+ }
+ description "NETCONF Error Severity";
+ reference "RFC 6241, Section 4.3";
+ }
+
+ typedef edit-operation-type {
+ type enumeration {
+ enum merge {
+ description
+ "The configuration data identified by the
+ element containing this attribute is merged
+ with the configuration at the corresponding
+ level in the configuration datastore identified
+ by the target parameter.";
+ }
+ enum replace {
+ description
+ "The configuration data identified by the element
+ containing this attribute replaces any related
+ configuration in the configuration datastore
+ identified by the target parameter. If no such
+ configuration data exists in the configuration
+ datastore, it is created. Unlike a
+ <copy-config> operation, which replaces the
+ entire target configuration, only the configuration
+ actually present in the config parameter is affected.";
+ }
+ enum create {
+ description
+ "The configuration data identified by the element
+ containing this attribute is added to the
+ configuration if and only if the configuration
+ data does not already exist in the configuration
+ datastore. If the configuration data exists, an
+ <rpc-error> element is returned with an
+ <error-tag> value of 'data-exists'.";
+ }
+ enum delete {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if and only if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, an <rpc-error> element is returned with
+ an <error-tag> value of 'data-missing'.";
+ }
+ enum remove {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, the 'remove' operation is silently ignored
+ by the server.";
+ }
+ }
+ default "merge";
+ description "NETCONF 'operation' attribute values";
+ reference "RFC 6241, Section 7.2";
+ }
+
+ // NETCONF Standard Protocol Operations
+
+ rpc get-config {
+ description
+ "Retrieve all or part of a specified configuration.";
+
+ reference "RFC 6241, Section 7.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to retrieve.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration to retrieve.";
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.
+ This is optional-to-implement on the server because
+ not all servers will support filtering for this
+ datastore.";
+ }
+ }
+ }
+
+ anyxml filter {
+ description
+ "Subtree or XPath filter to use.";
+ nc:get-filter-element-attributes;
+ // TODO this extension should be augmented (anyxml nodes cannot be augmented)
+ // or we can identify custom input/output arguments by schemaPath defined for custom handlers
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.reader.custom.FilterReader;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data container
+ indicates that the request did not produce any results.";
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.writer.custom.DataWriter;
+ }
+ }
+ }
+
+ rpc edit-config {
+ description
+ "The <edit-config> operation loads all or part of a specified
+ configuration to the specified target configuration.";
+
+ reference "RFC 6241, Section 7.2";
+
+ input {
+ container target {
+ description
+ "Particular configuration to edit.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ }
+ }
+
+ leaf default-operation {
+ type enumeration {
+ enum merge {
+ description
+ "The default operation is merge.";
+ }
+ enum replace {
+ description
+ "The default operation is replace.";
+ }
+ enum none {
+ description
+ "There is no default operation.";
+ }
+ }
+ default "merge";
+ description
+ "The default operation to use.";
+ }
+
+ leaf test-option {
+ if-feature validate;
+ type enumeration {
+ enum test-then-set {
+ description
+ "The server will test and then set if no errors.";
+ }
+ enum set {
+ description
+ "The server will set without a test first.";
+ }
+
+ enum test-only {
+ description
+ "The server will only test and not set, even
+ if there are no errors.";
+ }
+ }
+ default "test-then-set";
+ description
+ "The test option to use.";
+ }
+
+ leaf error-option {
+ type enumeration {
+ enum stop-on-error {
+ description
+ "The server will stop on errors.";
+ }
+ enum continue-on-error {
+ description
+ "The server may continue on errors.";
+ }
+ enum rollback-on-error {
+ description
+ "The server will roll back on errors.
+ This value can only be used if the 'rollback-on-error'
+ feature is supported.";
+ }
+ }
+ default "stop-on-error";
+ description
+ "The error option to use.";
+ }
+
+ choice edit-content {
+ mandatory true;
+ description
+ "The content for the edit operation.";
+
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.reader.custom.EditContentReader;
+
+ anyxml config {
+ description
+ "Inline Config content.";
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.reader.custom.ConfigReader;
+ }
+
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ rpc copy-config {
+ description
+ "Create or replace an entire configuration datastore with the
+ contents of another complete configuration datastore.";
+
+ reference "RFC 6241, Section 7.3";
+
+ input {
+ container target {
+ description
+ "Particular configuration to copy to.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target of the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config target.
+ This is optional-to-implement on the server.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+
+ container source {
+ description
+ "Particular configuration to copy from.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source for the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc delete-config {
+ description
+ "Delete a configuration datastore.";
+
+ reference "RFC 6241, Section 7.4";
+
+ input {
+ container target {
+ description
+ "Particular configuration to delete.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to delete.";
+
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc lock {
+ description
+ "The lock operation allows the client to lock the configuration
+ system of a device.";
+
+ reference "RFC 6241, Section 7.5";
+
+ input {
+ container target {
+ description
+ "Particular configuration to lock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to lock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc unlock {
+ description
+ "The unlock operation is used to release a configuration lock,
+ previously obtained with the 'lock' operation.";
+
+ reference "RFC 6241, Section 7.6";
+
+ input {
+ container target {
+ description
+ "Particular configuration to unlock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to unlock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc get {
+ description
+ "Retrieve running configuration and device state information.";
+
+ reference "RFC 6241, Section 7.7";
+
+ input {
+ anyxml filter {
+ description
+ "This parameter specifies the portion of the system
+ configuration and state data to retrieve.";
+ nc:get-filter-element-attributes;
+ // TODO this extension should be augmented (anyxml nodes cannot be augmented)
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.reader.custom.FilterReader;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+
+ cliext:argument-handler org.opendaylight.controller.netconf.cli.writer.custom.DataWriter;
+
+ }
+ }
+ }
+
+ rpc close-session {
+ description
+ "Request graceful termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.8";
+ }
+
+ rpc kill-session {
+ description
+ "Force the termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.9";
+
+ input {
+ leaf session-id {
+ type session-id-type;
+ mandatory true;
+ description
+ "Particular session to kill.";
+ }
+ }
+ }
+
+ rpc commit {
+ if-feature candidate;
+
+ description
+ "Commit the candidate configuration as the device's new
+ current configuration.";
+
+ reference "RFC 6241, Section 8.3.4.1";
+
+ input {
+ leaf confirmed {
+ if-feature confirmed-commit;
+ type empty;
+ description
+ "Requests a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf confirm-timeout {
+ if-feature confirmed-commit;
+ type uint32 {
+ range "1..max";
+ }
+ units "seconds";
+ default "600"; // 10 minutes
+ description
+ "The timeout interval for a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is used to make a confirmed commit
+ persistent. A persistent confirmed commit is not aborted
+ if the NETCONF session terminates. The only way to abort
+ a persistent confirmed commit is to let the timer expire,
+ or to use the <cancel-commit> operation.
+
+ The value of this parameter is a token that must be given
+ in the 'persist-id' parameter of <commit> or
+ <cancel-commit> operations in order to confirm or cancel
+ the persistent confirmed commit.
+
+ The token should be a random string.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist-id {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is given in order to commit a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ }
+ }
+
+ rpc discard-changes {
+ if-feature candidate;
+
+ description
+ "Revert the candidate configuration to the current
+ running configuration.";
+ reference "RFC 6241, Section 8.3.4.2";
+ }
+
+ rpc cancel-commit {
+ if-feature confirmed-commit;
+ description
+ "This operation is used to cancel an ongoing confirmed commit.
+ If the confirmed commit is persistent, the parameter
+ 'persist-id' must be given, and it must match the value of the
+ 'persist' parameter.";
+ reference "RFC 6241, Section 8.4.4.1";
+
+ input {
+ leaf persist-id {
+ type string;
+ description
+ "This parameter is given in order to cancel a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ }
+ }
+ }
+
+ rpc validate {
+ if-feature validate;
+
+ description
+ "Validates the contents of the specified configuration.";
+
+ reference "RFC 6241, Section 8.6.4.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to validate.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source to validate.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+}
--- /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.netconf.cli;
+
+import java.io.IOException;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.netconf.cli.io.ConsoleIOImpl;
+
+public class ConsoleIOTestImpl extends ConsoleIOImpl {
+
+ Map<String, Deque<String>> inputValues = new HashMap<>();
+ String lastMessage;
+ private final List<ValueForMessage> valuesForMessages;
+
+ public ConsoleIOTestImpl(final Map<String, Deque<String>> inputValues, final List<ValueForMessage> valuesForMessages)
+ throws IOException {
+ super();
+ this.inputValues = inputValues;
+ this.valuesForMessages = valuesForMessages;
+ }
+
+ StringBuilder output = new StringBuilder();
+
+ @Override
+ public String read() throws IOException {
+ final String prompt = buildPrompt();
+ output.append(prompt);
+ System.out.print(prompt);
+
+ String value = inputValues.get(prompt).pollFirst();
+ if (value == null) {
+ value = getValueForLastMessage();
+ }
+
+ value = value != null ? value : "****NO VALUE****";
+
+ output.append(value + "\n");
+ System.out.print(value + "\n");
+ return value;
+ }
+
+ private String getValueForLastMessage() {
+ for (final ValueForMessage valueForMessage : valuesForMessages) {
+ if (containsLastMessageKeyWords(valueForMessage.getKeyWords())) {
+ return valueForMessage.getValue();
+ }
+ }
+ return null;
+ }
+
+ private boolean containsLastMessageKeyWords(final List<String> keyWords) {
+ for (final String keyWord : keyWords) {
+ if (!lastMessage.contains(keyWord)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void write(final CharSequence data) throws IOException {
+ output.append(data);
+ lastMessage = (String) data;
+ System.out.print(data);
+ }
+
+ @Override
+ public void writeLn(final CharSequence data) throws IOException {
+ write(data);
+ output.append("\n");
+ System.out.print("\n");
+ }
+
+ public String getConsoleOutput() {
+ return output.toString();
+ }
+}
--- /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.netconf.cli;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.netconf.cli.io.IOUtil.PROMPT_SUFIX;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.cli.reader.ReadingException;
+import org.opendaylight.controller.netconf.cli.reader.impl.GenericReader;
+import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
+import org.opendaylight.controller.netconf.cli.writer.WriteException;
+import org.opendaylight.controller.netconf.cli.writer.impl.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+@Ignore
+public class NetconfCliTest {
+
+ private final static YangContextParser parser = new YangParserImpl();
+
+ private static SchemaContext loadSchemaContext(final String resourceDirectory) throws IOException,
+ URISyntaxException {
+ final URI uri = NetconfCliTest.class.getResource(resourceDirectory).toURI();
+ final File testDir = new File(uri);
+ final String[] fileList = testDir.list();
+ final List<File> testFiles = new ArrayList<File>();
+ if (fileList == null) {
+ throw new FileNotFoundException(resourceDirectory);
+ }
+ for (final String fileName : fileList) {
+ if (new File(testDir, fileName).isDirectory() == false) {
+ testFiles.add(new File(testDir, fileName));
+ }
+ }
+ return parser.parseFiles(testFiles);
+ }
+
+ // @Ignore
+ @Test
+ public void cliTest() throws ReadingException, IOException, WriteException, URISyntaxException {
+
+ final SchemaContext schemaContext = loadSchemaContext("/schema-context");
+ assertNotNull(schemaContext);
+
+ final DataSchemaNode cont1 = findTopLevelElement("ns:model1", "2014-05-14", "cont1", schemaContext);
+ final Map<String, Deque<String>> values = new HashMap<>();
+
+ values.put(prompt("/cont1/cont11/lst111/[entry]/lf1111"), value("55", "32"));
+ values.put(prompt("/cont1/cont11/lst111/[entry]"), value("Y", "Y"));
+ values.put(prompt("/cont1/cont11/lst111/[entry]/cont111/lf1112"),
+ value("value for lf1112", "2value for lf1112"));
+ values.put(prompt("/cont1/cont11/lst111/[entry]/cont111/lflst1111"), value("Y", "N", "Y", "N"));
+ values.put(prompt("/cont1/cont11/lst111/[entry]/cont111/lflst1111/[entry]"), value("10", "15", "20", "30"));
+
+ values.put(prompt("/cont1/cont11/lst111"), value("Y", "N"));
+
+ values.put(prompt("/cont1/cont12/chcA"), value("AB"));
+ values.put(prompt("/cont1/cont12/chcA/cont12AB1/lf12AB1"), value("value for lf12AB1"));
+
+ values.put(prompt("/cont1/cont12/lst121/[entry]/lf1211"), value("value for lf12112", "2value for lf12112"));
+ values.put(prompt("/cont1/cont12/lst121/[entry]"), value("Y", "Y"));
+ values.put(prompt("/cont1/cont12/lst121/[entry]/lst1211"), value("Y", "N", "Y", "N"));
+ values.put(prompt("/cont1/cont12/lst121/[entry]/lst1211/[entry]"), value("Y", "Y", "Y", "Y"));
+ values.put(prompt("/cont1/cont12/lst121/[entry]/lst1211/[entry]/lf12111"), value("5", "10", "21", "50"));
+ values.put(prompt("/cont1/cont12/lst121/[entry]/lst1211/[entry]/lf12112"),
+ value("value for lf12112", "2value for lf12112", "3value for lf12112", "4value for lf12112"));
+
+ values.put(prompt("/cont1/cont12/lst121"), value("Y", "N"));
+
+ values.put(prompt("/cont1/cont12/lst122"), value("Y", "N"));
+
+ values.put(prompt("/cont1/lst11"), value("Y", "Y", "N"));
+ values.put(prompt("/cont1/lst11/[entry]"), value("Y", "Y", "Y"));
+ values.put(prompt("/cont1/lst11/[entry]/lf111"),
+ value("1value for lf111", "2value for lf111", "3value for lf111"));
+
+ values.put(prompt("/cont1/cont12/data"), value("<el1><el11>value</el11><el12>value1</el12></el1>"));
+
+ final List<ValueForMessage> valuesForMessages = new ArrayList<>();
+ valuesForMessages.add(new ValueForMessage("Y", "lst111", "[Y|N]"));
+ valuesForMessages.add(new ValueForMessage("Y", "lst121", "[Y|N]"));
+ valuesForMessages.add(new ValueForMessage("Y", "lst11", "[Y|N]"));
+
+ final ConsoleIOTestImpl console = new ConsoleIOTestImpl(values, valuesForMessages);
+
+ final List<Node<?>> redData = new GenericReader(console, new CommandArgHandlerRegistry(console,
+ new SchemaContextRegistry(schemaContext)), schemaContext).read(cont1);
+ assertNotNull(redData);
+ assertEquals(1, redData.size());
+
+ assertTrue(redData.get(0) instanceof CompositeNode);
+ final CompositeNode redTopLevelNode = (CompositeNode) redData.get(0);
+
+ System.out.println("============================");
+ new NormalizedNodeWriter(console, new OutFormatter()).write(cont1, redData);
+
+ }
+
+ private Deque<String> value(final String... values) {
+ return new ArrayDeque<>(Arrays.asList(values));
+ }
+
+ private String prompt(final String path) {
+ return "/localhost" + path + PROMPT_SUFIX;
+ }
+
+ private DataSchemaNode findTopLevelElement(final String namespace, final String revision,
+ final String topLevelElement, final SchemaContext schemaContext) {
+ final QName requiredElement = QName.create(namespace, revision, topLevelElement);
+ for (final DataSchemaNode dataSchemaNode : schemaContext.getChildNodes()) {
+ if (dataSchemaNode.getQName().equals(requiredElement)) {
+ return dataSchemaNode;
+ }
+ }
+ return null;
+
+ }
+
+}
--- /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.netconf.cli;
+
+import java.util.Arrays;
+import java.util.List;
+
+class ValueForMessage {
+ List<String> messageKeyWords;
+ String value;
+
+ public ValueForMessage(final String value, final String... messageKeyWords) {
+ this.messageKeyWords = Arrays.asList(messageKeyWords);
+ this.value = value;
+ }
+
+ public List<String> getKeyWords() {
+ return messageKeyWords;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
--- /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.netconf.cli.io;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.cli.commands.CommandConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class IOUtilTest {
+
+ @Test
+ public void testQNameFromKeyStringNew() throws Exception {
+ final String s = IOUtil.qNameToKeyString(CommandConstants.HELP_QNAME, "module");
+ final Map<String, QName> modulesMap = Maps.newHashMap();
+ modulesMap.put("module", new QName(CommandConstants.HELP_QNAME, "module"));
+ final QName qName = IOUtil.qNameFromKeyString(s, modulesMap);
+ Assert.assertEquals(CommandConstants.HELP_QNAME, qName);
+ }
+}
--- /dev/null
+ module ietf-inet-types {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+ prefix "inet";
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: David Partain
+ <mailto:david.partain@ericsson.com>
+
+ WG Chair: David Kessens
+ <mailto:david.kessens@nsn.com>
+
+ Editor: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>";
+
+ description
+ "This module contains a collection of generally useful derived
+ YANG data types for Internet addresses and related things.
+
+ Copyright (c) 2010 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, is permitted pursuant to, and subject to the license
+ terms contained in, the Simplified BSD License set forth in Section
+ 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6021; see
+ the RFC itself for full legal notices.";
+
+ revision 2010-09-24 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 6021: Common YANG Data Types";
+ }
+
+ /*** collection of protocol field related types ***/
+
+ typedef ip-version {
+ type enumeration {
+ enum unknown {
+ value "0";
+ description
+ "An unknown or unspecified version of the Internet protocol.";
+ }
+ enum ipv4 {
+ value "1";
+ description
+ "The IPv4 protocol as defined in RFC 791.";
+ }
+ enum ipv6 {
+ value "2";
+ description
+ "The IPv6 protocol as defined in RFC 2460.";
+ }
+ }
+ description
+ "This value represents the version of the IP protocol.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetVersion textual convention of the SMIv2.";
+ reference
+ "RFC 791: Internet Protocol
+ RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ typedef dscp {
+ type uint8 {
+ range "0..63";
+ }
+ description
+ "The dscp type represents a Differentiated Services Code-Point
+ that may be used for marking packets in a traffic stream.
+
+ In the value set and its semantics, this type is equivalent
+ to the Dscp textual convention of the SMIv2.";
+ reference
+ "RFC 3289: Management Information Base for the Differentiated
+ Services Architecture
+ RFC 2474: Definition of the Differentiated Services Field
+ (DS Field) in the IPv4 and IPv6 Headers
+ RFC 2780: IANA Allocation Guidelines For Values In
+ the Internet Protocol and Related Headers";
+ }
+
+ typedef ipv6-flow-label {
+ type uint32 {
+ range "0..1048575";
+ }
+ description
+ "The flow-label type represents flow identifier or Flow Label
+ in an IPv6 packet header that may be used to discriminate
+ traffic flows.
+
+ In the value set and its semantics, this type is equivalent
+ to the IPv6FlowLabel textual convention of the SMIv2.";
+ reference
+ "RFC 3595: Textual Conventions for IPv6 Flow Label
+ RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+ }
+
+ typedef port-number {
+ type uint16 {
+ range "0..65535";
+ }
+ description
+ "The port-number type represents a 16-bit port number of an
+ Internet transport layer protocol such as UDP, TCP, DCCP, or
+ SCTP. Port numbers are assigned by IANA. A current list of
+ all assignments is available from <http://www.iana.org/>.
+
+ Note that the port number value zero is reserved by IANA. In
+ situations where the value zero does not make sense, it can
+ be excluded by subtyping the port-number type.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetPortNumber textual convention of the SMIv2.";
+ reference
+ "RFC 768: User Datagram Protocol
+ RFC 793: Transmission Control Protocol
+ RFC 4960: Stream Control Transmission Protocol
+ RFC 4340: Datagram Congestion Control Protocol (DCCP)
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ /*** collection of autonomous system related types ***/
+
+ typedef as-number {
+ type uint32;
+ description
+ "The as-number type represents autonomous system numbers
+ which identify an Autonomous System (AS). An AS is a set
+ of routers under a single technical administration, using
+ an interior gateway protocol and common metrics to route
+ packets within the AS, and using an exterior gateway
+ protocol to route packets to other ASs'. IANA maintains
+ the AS number space and has delegated large parts to the
+ regional registries.
+
+ Autonomous system numbers were originally limited to 16
+ bits. BGP extensions have enlarged the autonomous system
+ number space to 32 bits. This type therefore uses an uint32
+ base type without a range restriction in order to support
+ a larger autonomous system number space.
+
+ In the value set and its semantics, this type is equivalent
+ to the InetAutonomousSystemNumber textual convention of
+ the SMIv2.";
+ reference
+ "RFC 1930: Guidelines for creation, selection, and registration
+ of an Autonomous System (AS)
+ RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+ RFC 4893: BGP Support for Four-octet AS Number Space
+ RFC 4001: Textual Conventions for Internet Network Addresses";
+ }
+
+ /*** collection of IP address and hostname related types ***/
+
+ typedef ip-address {
+ type union {
+ type inet:ipv4-address;
+ type inet:ipv6-address;
+ }
+ description
+ "The ip-address type represents an IP address and is IP
+ version neutral. The format of the textual representations
+ implies the IP version.";
+ }
+
+ typedef ipv4-address {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '(%[\p{N}\p{L}]+)?';
+ }
+ description
+ "The ipv4-address type represents an IPv4 address in
+ dotted-quad notation. The IPv4 address may include a zone
+ index, separated by a % sign.
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format for the zone index is the numerical
+ format";
+ }
+
+ typedef ipv6-address {
+ type string {
+ pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ + '(%[\p{N}\p{L}]+)?';
+ pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ + '(%.+)?';
+ }
+ description
+ "The ipv6-address type represents an IPv6 address in full,
+ mixed, shortened, and shortened-mixed notation. The IPv6
+ address may include a zone index, separated by a % sign.
+
+ The zone index is used to disambiguate identical address
+ values. For link-local addresses, the zone index will
+ typically be the interface index number or the name of an
+ interface. If the zone index is not present, the default
+ zone of the device will be used.
+
+ The canonical format of IPv6 addresses uses the compressed
+ format described in RFC 4291, Section 2.2, item 2 with the
+ following additional rules: the :: substitution must be
+ applied to the longest sequence of all-zero 16-bit chunks
+ in an IPv6 address. If there is a tie, the first sequence
+ of all-zero 16-bit chunks is replaced by ::. Single
+ all-zero 16-bit chunks are not compressed. The canonical
+ format uses lowercase characters and leading zeros are
+ not allowed. The canonical format for the zone index is
+ the numerical format as described in RFC 4007, Section
+ 11.2.";
+ reference
+ "RFC 4291: IP Version 6 Addressing Architecture
+ RFC 4007: IPv6 Scoped Address Architecture
+ RFC 5952: A Recommendation for IPv6 Address Text Representation";
+ }
+
+ typedef ip-prefix {
+ type union {
+ type inet:ipv4-prefix;
+ type inet:ipv6-prefix;
+ }
+ description
+ "The ip-prefix type represents an IP prefix and is IP
+ version neutral. The format of the textual representations
+ implies the IP version.";
+ }
+
+ typedef ipv4-prefix {
+ type string {
+ pattern
+ '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+ }
+ description
+ "The ipv4-prefix type represents an IPv4 address prefix.
+ The prefix length is given by the number following the
+ slash character and must be less than or equal to 32.
+
+ A prefix length value of n corresponds to an IP address
+ mask that has n contiguous 1-bits from the most
+ significant bit (MSB) and all other bits set to 0.
+
+ The canonical format of an IPv4 prefix has all bits of
+ the IPv4 address set to zero that are not part of the
+ IPv4 prefix.";
+ }
+
+ typedef ipv6-prefix {
+ type string {
+ pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+ pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ + '(/.+)';
+ }
+ description
+ "The ipv6-prefix type represents an IPv6 address prefix.
+ The prefix length is given by the number following the
+ slash character and must be less than or equal 128.
+
+ A prefix length value of n corresponds to an IP address
+ mask that has n contiguous 1-bits from the most
+ significant bit (MSB) and all other bits set to 0.
+
+ The IPv6 address should have all bits that do not belong
+ to the prefix set to zero.
+
+ The canonical format of an IPv6 prefix has all bits of
+ the IPv6 address set to zero that are not part of the
+ IPv6 prefix. Furthermore, IPv6 address is represented
+ in the compressed format described in RFC 4291, Section
+ 2.2, item 2 with the following additional rules: the ::
+ substitution must be applied to the longest sequence of
+ all-zero 16-bit chunks in an IPv6 address. If there is
+ a tie, the first sequence of all-zero 16-bit chunks is
+ replaced by ::. Single all-zero 16-bit chunks are not
+ compressed. The canonical format uses lowercase
+ characters and leading zeros are not allowed.";
+ reference
+ "RFC 4291: IP Version 6 Addressing Architecture";
+ }
+
+ /*** collection of domain name and URI types ***/
+
+ typedef domain-name {
+ type string {
+ pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+ + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+ + '|\.';
+ length "1..253";
+ }
+ description
+ "The domain-name type represents a DNS domain name. The
+ name SHOULD be fully qualified whenever possible.
+
+ Internet domain names are only loosely specified. Section
+ 3.5 of RFC 1034 recommends a syntax (modified in Section
+ 2.1 of RFC 1123). The pattern above is intended to allow
+ for current practice in domain name use, and some possible
+ future expansion. It is designed to hold various types of
+ domain names, including names used for A or AAAA records
+ (host names) and other records, such as SRV records. Note
+ that Internet host names have a stricter syntax (described
+ in RFC 952) than the DNS recommendations in RFCs 1034 and
+ 1123, and that systems that want to store host names in
+ schema nodes using the domain-name type are recommended to
+ adhere to this stricter standard to ensure interoperability.
+
+ The encoding of DNS names in the DNS protocol is limited
+ to 255 characters. Since the encoding consists of labels
+ prefixed by a length bytes and there is a trailing NULL
+ byte, only 253 characters can appear in the textual dotted
+ notation.
+
+ The description clause of schema nodes using the domain-name
+ type MUST describe when and how these names are resolved to
+ IP addresses. Note that the resolution of a domain-name value
+ may require to query multiple DNS records (e.g., A for IPv4
+ and AAAA for IPv6). The order of the resolution process and
+ which DNS record takes precedence can either be defined
+ explicitely or it may depend on the configuration of the
+ resolver.
+
+ Domain-name values use the US-ASCII encoding. Their canonical
+ format uses lowercase US-ASCII characters. Internationalized
+ domain names MUST be encoded in punycode as described in RFC
+ 3492";
+ reference
+ "RFC 952: DoD Internet Host Table Specification
+ RFC 1034: Domain Names - Concepts and Facilities
+ RFC 1123: Requirements for Internet Hosts -- Application
+ and Support
+ RFC 2782: A DNS RR for specifying the location of services
+ (DNS SRV)
+ RFC 3492: Punycode: A Bootstring encoding of Unicode for
+ Internationalized Domain Names in Applications
+ (IDNA)
+ RFC 5891: Internationalizing Domain Names in Applications
+ (IDNA): Protocol";
+ }
+
+ typedef host {
+ type union {
+ type inet:ip-address;
+ type inet:domain-name;
+ }
+ description
+ "The host type represents either an IP address or a DNS
+ domain name.";
+ }
+
+ typedef uri {
+ type string;
+ description
+ "The uri type represents a Uniform Resource Identifier
+ (URI) as defined by STD 66.
+
+ Objects using the uri type MUST be in US-ASCII encoding,
+ and MUST be normalized as described by RFC 3986 Sections
+ 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary
+ percent-encoding is removed, and all case-insensitive
+ characters are set to lowercase except for hexadecimal
+ digits, which are normalized to uppercase as described in
+ Section 6.2.2.1.
+
+ The purpose of this normalization is to help provide
+ unique URIs. Note that this normalization is not
+ sufficient to provide uniqueness. Two URIs that are
+ textually distinct after this normalization may still be
+ equivalent.
+
+ Objects using the uri type may restrict the schemes that
+ they permit. For example, 'data:' and 'urn:' schemes
+ might not be appropriate.
+
+ A zero-length URI is not a valid URI. This can be used to
+ express 'URI absent' where required.
+
+ In the value set and its semantics, this type is equivalent
+ to the Uri SMIv2 textual convention defined in RFC 5017.";
+ reference
+ "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+ RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+ Group: Uniform Resource Identifiers (URIs), URLs,
+ and Uniform Resource Names (URNs): Clarifications
+ and Recommendations
+ RFC 5017: MIB Textual Conventions for Uniform Resource
+ Identifiers (URIs)";
+ }
+
+ }
--- /dev/null
+module ietf-netconf {
+
+ // the namespace for NETCONF XML definitions is unchanged
+ // from RFC 4741, which this document replaces
+ namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+ prefix nc;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Martin Bjorklund
+ <mbj@tail-f.com>
+
+ Editor: Juergen Schoenwaelder
+ <j.schoenwaelder@jacobs-university.de>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>";
+ description
+ "NETCONF Protocol Data Types and Protocol Operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6241; see
+ the RFC itself for full legal notices.";
+ revision 2011-06-01 {
+ description
+ "Initial revision";
+ reference
+ "RFC 6241: Network Configuration Protocol";
+ }
+
+ extension get-filter-element-attributes {
+ description
+ "If this extension is present within an 'anyxml'
+ statement named 'filter', which must be conceptually
+ defined within the RPC input section for the <get>
+ and <get-config> protocol operations, then the
+ following unqualified XML attribute is supported
+ within the <filter> element, within a <get> or
+ <get-config> protocol operation:
+
+ type : optional attribute with allowed
+ value strings 'subtree' and 'xpath'.
+ If missing, the default value is 'subtree'.
+
+ If the 'xpath' feature is supported, then the
+ following unqualified XML attribute is
+ also supported:
+
+ select: optional attribute containing a
+ string representing an XPath expression.
+ The 'type' attribute must be equal to 'xpath'
+ if this attribute is present.";
+ }
+
+ // NETCONF capabilities defined as features
+ feature writable-running {
+ description
+ "NETCONF :writable-running capability;
+ If the server advertises the :writable-running
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.2";
+ }
+
+ feature candidate {
+ description
+ "NETCONF :candidate capability;
+ If the server advertises the :candidate
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.3";
+ }
+
+ feature confirmed-commit {
+ if-feature candidate;
+ description
+ "NETCONF :confirmed-commit:1.1 capability;
+ If the server advertises the :confirmed-commit:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+
+ reference "RFC 6241, Section 8.4";
+ }
+
+ feature rollback-on-error {
+ description
+ "NETCONF :rollback-on-error capability;
+ If the server advertises the :rollback-on-error
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.5";
+ }
+
+ feature validate {
+ description
+ "NETCONF :validate:1.1 capability;
+ If the server advertises the :validate:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.6";
+ }
+
+ feature startup {
+ description
+ "NETCONF :startup capability;
+ If the server advertises the :startup
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.7";
+ }
+
+ feature url {
+ description
+ "NETCONF :url capability;
+ If the server advertises the :url
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.8";
+ }
+
+ feature xpath {
+ description
+ "NETCONF :xpath capability;
+ If the server advertises the :xpath
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.9";
+ }
+
+ // NETCONF Simple Types
+
+ typedef session-id-type {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "NETCONF Session Id";
+ }
+
+ typedef session-id-or-zero-type {
+ type uint32;
+ description
+ "NETCONF Session Id or Zero to indicate none";
+ }
+ typedef error-tag-type {
+ type enumeration {
+ enum in-use {
+ description
+ "The request requires a resource that
+ already is in use.";
+ }
+ enum invalid-value {
+ description
+ "The request specifies an unacceptable value for one
+ or more parameters.";
+ }
+ enum too-big {
+ description
+ "The request or response (that would be generated) is
+ too large for the implementation to handle.";
+ }
+ enum missing-attribute {
+ description
+ "An expected attribute is missing.";
+ }
+ enum bad-attribute {
+ description
+ "An attribute value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-attribute {
+ description
+ "An unexpected attribute is present.";
+ }
+ enum missing-element {
+ description
+ "An expected element is missing.";
+ }
+ enum bad-element {
+ description
+ "An element value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-element {
+ description
+ "An unexpected element is present.";
+ }
+ enum unknown-namespace {
+ description
+ "An unexpected namespace is present.";
+ }
+ enum access-denied {
+ description
+ "Access to the requested protocol operation or
+ data model is denied because authorization failed.";
+ }
+ enum lock-denied {
+ description
+ "Access to the requested lock is denied because the
+ lock is currently held by another entity.";
+ }
+ enum resource-denied {
+ description
+ "Request could not be completed because of
+ insufficient resources.";
+ }
+ enum rollback-failed {
+ description
+ "Request to roll back some configuration change (via
+ rollback-on-error or <discard-changes> operations)
+ was not completed for some reason.";
+
+ }
+ enum data-exists {
+ description
+ "Request could not be completed because the relevant
+ data model content already exists. For example,
+ a 'create' operation was attempted on data that
+ already exists.";
+ }
+ enum data-missing {
+ description
+ "Request could not be completed because the relevant
+ data model content does not exist. For example,
+ a 'delete' operation was attempted on
+ data that does not exist.";
+ }
+ enum operation-not-supported {
+ description
+ "Request could not be completed because the requested
+ operation is not supported by this implementation.";
+ }
+ enum operation-failed {
+ description
+ "Request could not be completed because the requested
+ operation failed for some reason not covered by
+ any other error condition.";
+ }
+ enum partial-operation {
+ description
+ "This error-tag is obsolete, and SHOULD NOT be sent
+ by servers conforming to this document.";
+ }
+ enum malformed-message {
+ description
+ "A message could not be handled because it failed to
+ be parsed correctly. For example, the message is not
+ well-formed XML or it uses an invalid character set.";
+ }
+ }
+ description "NETCONF Error Tag";
+ reference "RFC 6241, Appendix A";
+ }
+
+ typedef error-severity-type {
+ type enumeration {
+ enum error {
+ description "Error severity";
+ }
+ enum warning {
+ description "Warning severity";
+ }
+ }
+ description "NETCONF Error Severity";
+ reference "RFC 6241, Section 4.3";
+ }
+
+ typedef edit-operation-type {
+ type enumeration {
+ enum merge {
+ description
+ "The configuration data identified by the
+ element containing this attribute is merged
+ with the configuration at the corresponding
+ level in the configuration datastore identified
+ by the target parameter.";
+ }
+ enum replace {
+ description
+ "The configuration data identified by the element
+ containing this attribute replaces any related
+ configuration in the configuration datastore
+ identified by the target parameter. If no such
+ configuration data exists in the configuration
+ datastore, it is created. Unlike a
+ <copy-config> operation, which replaces the
+ entire target configuration, only the configuration
+ actually present in the config parameter is affected.";
+ }
+ enum create {
+ description
+ "The configuration data identified by the element
+ containing this attribute is added to the
+ configuration if and only if the configuration
+ data does not already exist in the configuration
+ datastore. If the configuration data exists, an
+ <rpc-error> element is returned with an
+ <error-tag> value of 'data-exists'.";
+ }
+ enum delete {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if and only if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, an <rpc-error> element is returned with
+ an <error-tag> value of 'data-missing'.";
+ }
+ enum remove {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, the 'remove' operation is silently ignored
+ by the server.";
+ }
+ }
+ default "merge";
+ description "NETCONF 'operation' attribute values";
+ reference "RFC 6241, Section 7.2";
+ }
+
+ // NETCONF Standard Protocol Operations
+
+ rpc get-config {
+ description
+ "Retrieve all or part of a specified configuration.";
+
+ reference "RFC 6241, Section 7.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to retrieve.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration to retrieve.";
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.
+ This is optional-to-implement on the server because
+ not all servers will support filtering for this
+ datastore.";
+ }
+ }
+ }
+
+ anyxml filter {
+ description
+ "Subtree or XPath filter to use.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data container
+ indicates that the request did not produce any results.";
+ }
+ }
+ }
+
+ rpc edit-config {
+ description
+ "The <edit-config> operation loads all or part of a specified
+ configuration to the specified target configuration.";
+
+ reference "RFC 6241, Section 7.2";
+
+ input {
+ container target {
+ description
+ "Particular configuration to edit.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ }
+ }
+
+ leaf default-operation {
+ type enumeration {
+ enum merge {
+ description
+ "The default operation is merge.";
+ }
+ enum replace {
+ description
+ "The default operation is replace.";
+ }
+ enum none {
+ description
+ "There is no default operation.";
+ }
+ }
+ default "merge";
+ description
+ "The default operation to use.";
+ }
+
+ leaf test-option {
+ if-feature validate;
+ type enumeration {
+ enum test-then-set {
+ description
+ "The server will test and then set if no errors.";
+ }
+ enum set {
+ description
+ "The server will set without a test first.";
+ }
+
+ enum test-only {
+ description
+ "The server will only test and not set, even
+ if there are no errors.";
+ }
+ }
+ default "test-then-set";
+ description
+ "The test option to use.";
+ }
+
+ leaf error-option {
+ type enumeration {
+ enum stop-on-error {
+ description
+ "The server will stop on errors.";
+ }
+ enum continue-on-error {
+ description
+ "The server may continue on errors.";
+ }
+ enum rollback-on-error {
+ description
+ "The server will roll back on errors.
+ This value can only be used if the 'rollback-on-error'
+ feature is supported.";
+ }
+ }
+ default "stop-on-error";
+ description
+ "The error option to use.";
+ }
+
+ choice edit-content {
+ mandatory true;
+ description
+ "The content for the edit operation.";
+
+ anyxml config {
+ description
+ "Inline Config content.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ rpc copy-config {
+ description
+ "Create or replace an entire configuration datastore with the
+ contents of another complete configuration datastore.";
+
+ reference "RFC 6241, Section 7.3";
+
+ input {
+ container target {
+ description
+ "Particular configuration to copy to.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target of the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config target.
+ This is optional-to-implement on the server.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+
+ container source {
+ description
+ "Particular configuration to copy from.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source for the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc delete-config {
+ description
+ "Delete a configuration datastore.";
+
+ reference "RFC 6241, Section 7.4";
+
+ input {
+ container target {
+ description
+ "Particular configuration to delete.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to delete.";
+
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc lock {
+ description
+ "The lock operation allows the client to lock the configuration
+ system of a device.";
+
+ reference "RFC 6241, Section 7.5";
+
+ input {
+ container target {
+ description
+ "Particular configuration to lock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to lock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc unlock {
+ description
+ "The unlock operation is used to release a configuration lock,
+ previously obtained with the 'lock' operation.";
+
+ reference "RFC 6241, Section 7.6";
+
+ input {
+ container target {
+ description
+ "Particular configuration to unlock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to unlock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc get {
+ description
+ "Retrieve running configuration and device state information.";
+
+ reference "RFC 6241, Section 7.7";
+
+ input {
+ anyxml filter {
+ description
+ "This parameter specifies the portion of the system
+ configuration and state data to retrieve.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+
+ rpc close-session {
+ description
+ "Request graceful termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.8";
+ }
+
+ rpc kill-session {
+ description
+ "Force the termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.9";
+
+ input {
+ leaf session-id {
+ type session-id-type;
+ mandatory true;
+ description
+ "Particular session to kill.";
+ }
+ }
+ }
+
+ rpc commit {
+ if-feature candidate;
+
+ description
+ "Commit the candidate configuration as the device's new
+ current configuration.";
+
+ reference "RFC 6241, Section 8.3.4.1";
+
+ input {
+ leaf confirmed {
+ if-feature confirmed-commit;
+ type empty;
+ description
+ "Requests a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf confirm-timeout {
+ if-feature confirmed-commit;
+ type uint32 {
+ range "1..max";
+ }
+ units "seconds";
+ default "600"; // 10 minutes
+ description
+ "The timeout interval for a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is used to make a confirmed commit
+ persistent. A persistent confirmed commit is not aborted
+ if the NETCONF session terminates. The only way to abort
+ a persistent confirmed commit is to let the timer expire,
+ or to use the <cancel-commit> operation.
+
+ The value of this parameter is a token that must be given
+ in the 'persist-id' parameter of <commit> or
+ <cancel-commit> operations in order to confirm or cancel
+ the persistent confirmed commit.
+
+ The token should be a random string.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist-id {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is given in order to commit a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ }
+ }
+
+ rpc discard-changes {
+ if-feature candidate;
+
+ description
+ "Revert the candidate configuration to the current
+ running configuration.";
+ reference "RFC 6241, Section 8.3.4.2";
+ }
+
+ rpc cancel-commit {
+ if-feature confirmed-commit;
+ description
+ "This operation is used to cancel an ongoing confirmed commit.
+ If the confirmed commit is persistent, the parameter
+ 'persist-id' must be given, and it must match the value of the
+ 'persist' parameter.";
+ reference "RFC 6241, Section 8.4.4.1";
+
+ input {
+ leaf persist-id {
+ type string;
+ description
+ "This parameter is given in order to cancel a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ }
+ }
+ }
+
+ rpc validate {
+ if-feature validate;
+
+ description
+ "Validates the contents of the specified configuration.";
+
+ reference "RFC 6241, Section 8.6.4.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to validate.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source to validate.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+}
--- /dev/null
+module model1 {
+ namespace "ns:model1";
+ prefix "mod1";
+
+ revision "2014-05-14" {
+ }
+
+ container cont1 {
+ container cont11 {
+ list lst111 {
+ key lf1111;
+ leaf lf1111 {
+ type int32;
+ }
+ container cont111 {
+ leaf lf1112 {
+ type string;
+ }
+ leaf-list lflst1111 {
+ type int8;
+ }
+ }
+ }
+ }
+
+ container cont12 {
+ list lst121 {
+ key lf1211;
+ leaf lf1211 {
+ type string;
+ }
+ list lst1211 {
+ leaf lf12111 {
+ type uint8;
+ }
+ leaf lf12112 {
+ type string;
+ }
+ }
+ }
+ choice chcA {
+ case AA {
+ leaf lf12AA1 {
+ type string;
+ }
+ }
+ case AB {
+ container cont12AB1 {
+ leaf lf12AB1 {
+ type string;
+ }
+ }
+ }
+ leaf lf121 { //should be standalone case
+ type string;
+ }
+ }
+ list lst122 {
+ }
+ }
+ }
+
+ container cont2 {
+ container cont23 {
+ }
+ }
+
+ container contA {
+ }
+
+ container contB {
+ }
+
+}
\ No newline at end of file
--- /dev/null
+module model2 {
+ namespace "ns:model2";
+ prefix "mod2";
+
+ import model1 {prefix model1; revision-date 2014-05-14;}
+
+ revision "2014-05-14" {
+ }
+
+ container contA {
+ }
+
+ container contB {
+ }
+
+ augment "/model1:cont2" {
+ container cont21 {
+ }
+
+ container cont22 {
+ }
+ }
+
+ augment "/model1:cont1" {
+ list lst11 {
+ leaf lf111 {
+ type string;
+ }
+ }
+ }
+
+}
\ No newline at end of file
}
notificationVerifier.assertNotificationCount(2);
- notificationVerifier.assertNotificationContent(0, 0, 0, 9);
- notificationVerifier.assertNotificationContent(1, 4, 3, 9);
+ notificationVerifier.assertNotificationContent(0, 0, 0, 8);
+ notificationVerifier.assertNotificationContent(1, 4, 3, 8);
mockedAggregator.assertSnapshotCount(2);
// Capabilities are stripped for persister
private final class ExceptionHandlingInboundChannelHandler extends ChannelInboundHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
- logger.warn("An exception occurred during negotiation on channel {}", channel.localAddress(), cause);
+ logger.warn("An exception occurred during negotiation with {}", channel.remoteAddress(), cause);
cancelTimeout();
negotiationFailed(cause);
changeState(State.FAILED);
<modules>
<module>netconf-api</module>
+ <module>netconf-cli</module>
<module>netconf-impl</module>
<module>config-netconf-connector</module>
<module>netconf-util</module>
--- /dev/null
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * 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
+ *
+ * Authors : Dave Tucker
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+/**
+ * This class contains behaviour common to Neutron configuration objects
+ */
+public interface INeutronObject {
+ public String getID();
+ public void setID(String id);
+}
@XmlRootElement(name = "network")
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronNetwork extends ConfigurationObject implements Serializable {
+public class NeutronNetwork extends ConfigurationObject implements Serializable, INeutronObject {
// See OpenStack Network API v2.0 Reference for description of
// annotated attributes
public String getID() { return networkUUID; }
+ public void setID(String id) { this.networkUUID = id; }
+
public String getNetworkUUID() {
return networkUUID;
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronPort extends ConfigurationObject implements Serializable {
+public class NeutronPort extends ConfigurationObject implements Serializable, INeutronObject {
private static final long serialVersionUID = 1L;
// See OpenStack Network API v2.0 Reference for description of
public String getID() { return portUUID; }
+ public void setID(String id) { this.portUUID = id; }
+
public String getPortUUID() {
return portUUID;
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronRouter extends ConfigurationObject implements Serializable {
+public class NeutronRouter extends ConfigurationObject implements Serializable, INeutronObject {
private static final long serialVersionUID = 1L;
// See OpenStack Network API v2.0 Reference for description of
public String getID() { return routerUUID; }
+ public void setID(String id) { this.routerUUID = id; }
+
public String getRouterUUID() {
return routerUUID;
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronSubnet extends ConfigurationObject implements Serializable {
+public class NeutronSubnet extends ConfigurationObject implements Serializable, INeutronObject {
private static final long serialVersionUID = 1L;
// See OpenStack Network API v2.0 Reference for description of
public String getID() { return subnetUUID; }
+ public void setID(String id) { this.subnetUUID = id; }
+
public String getSubnetUUID() {
return subnetUUID;
}
--- /dev/null
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * 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
+ *
+ * Authors : Dave Tucker
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.northbound;
+
+import org.opendaylight.controller.networkconfig.neutron.INeutronObject;
+
+import java.util.List;
+
+public interface INeutronRequest<T extends INeutronObject> {
+ public T getSingleton();
+ public boolean isSingleton();
+ public List<T> getBulk();
+}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronNetworkRequest {
+public class NeutronNetworkRequest implements INeutronRequest {
// See OpenStack Network API v2.0 Reference for description of
// annotated attributes
@XmlElement(name="networks")
List<NeutronNetwork> bulkRequest;
+ @XmlElement(name="networks_links")
+ List<NeutronPageLink> links;
+
NeutronNetworkRequest() {
}
+ NeutronNetworkRequest(List<NeutronNetwork> bulkRequest, List<NeutronPageLink> links) {
+ this.bulkRequest = bulkRequest;
+ this.links = links;
+ this.singletonNetwork = null;
+ }
+
NeutronNetworkRequest(List<NeutronNetwork> bulk) {
bulkRequest = bulk;
singletonNetwork = null;
package org.opendaylight.controller.networkconfig.neutron.northbound;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.codehaus.enunciate.jaxrs.StatusCodes;
import org.codehaus.enunciate.jaxrs.TypeHint;
+
import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkAware;
import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
@QueryParam("provider_network_type") String queryProviderNetworkType,
@QueryParam("provider_physical_network") String queryProviderPhysicalNetwork,
@QueryParam("provider_segmentation_id") String queryProviderSegmentationID,
- // pagination
+ // linkTitle
@QueryParam("limit") Integer limit,
@QueryParam("marker") String marker,
@DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
// sorting not supported
) {
- INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD( this);
+ INeutronNetworkCRUD networkInterface = NeutronCRUDInterfaces.getINeutronNetworkCRUD(this);
if (networkInterface == null) {
throw new ServiceUnavailableException("Network CRUD Interface "
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
}
- Comparator<NeutronNetwork> neutronNetworkComparator = new Comparator<NeutronNetwork>() {
- @Override
- public int compare(NeutronNetwork o1, NeutronNetwork o2) {
- return o1.getID().compareTo(o2.getID());
- }
- };
-
- Collections.sort(ans, neutronNetworkComparator);
-
if (limit != null && ans.size() > 1) {
- List<NeutronPageLink> links = new ArrayList<>();
- Integer startPos = null;
- String startMarker;
- String endMarker;
- Boolean firstPage = false;
- Boolean lastPage = false;
-
- if (marker == null) {
- startPos = 0;
- }
-
- else {
-
- NeutronNetwork markerNetwork = new NeutronNetwork();
- markerNetwork.setNetworkUUID(marker);
-
- startPos = Collections.binarySearch(ans, markerNetwork, neutronNetworkComparator);
-
- if (!pageReverse){
- startPos = startPos + 1;
- }
- else {
- startPos = startPos - limit;
- }
-
- }
-
- if (startPos == null) {
- throw new ResourceNotFoundException("UUID for marker:" + marker + " could not be found");
- }
-
- if (startPos == 0){
- firstPage = true;
- }
-
- if (startPos + limit >= ans.size()) {
- ans = ans.subList(startPos, ans.size());
- startMarker = ans.get(0).getID();
- endMarker = ans.get(ans.size() - 1).getID();
- lastPage = true;
- }
- else if (startPos < 0) {
- if (startPos + limit > 0) {
- ans = ans.subList(0, startPos + limit);
- startMarker = ans.get(0).getID();
- endMarker = ans.get(ans.size() - 1).getID();
- firstPage = true;
- }
- else {
- throw new BadRequestException("Requested page is out of bounds. Please check the supplied limit and marker");
- }
- }
- else {
- ans = ans.subList(startPos, startPos + limit);
- startMarker = ans.get(0).getID();
- endMarker = ans.get(limit-1).getID();
- }
-
- if (!lastPage) {
- NeutronPageLink next = new NeutronPageLink();
- next.setRef("next");
- next.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + endMarker);
- links.add(next);
- }
-
- if (!firstPage) {
- NeutronPageLink previous = new NeutronPageLink();
- previous.setRef("previous");
- previous.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + startMarker + "&page_reverse=True");
- links.add(previous);
- }
-
- return Response.status(200).entity(new PaginatedNeutronNetworkRequest(ans, links)).build();
+ // Return a paginated request
+ NeutronNetworkRequest request = (NeutronNetworkRequest) PaginatedRequestFactory.createRequest(limit,
+ marker, pageReverse, uriInfo, ans, NeutronNetwork.class);
+ return Response.status(200).entity(request).build();
}
return Response.status(200).entity(new NeutronNetworkRequest(ans)).build();
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronPortRequest {
+public class NeutronPortRequest implements INeutronRequest {
// See OpenStack Network API v2.0 Reference for description of
// annotated attributes
@XmlElement(name="ports")
List<NeutronPort> bulkRequest;
+ @XmlElement(name="ports_links")
+ List<NeutronPageLink> links;
+
NeutronPortRequest() {
}
+ public NeutronPortRequest(List<NeutronPort> bulkRequest, List<NeutronPageLink> links) {
+ this.bulkRequest = bulkRequest;
+ this.links = links;
+ this.singletonPort = null;
+ }
+
NeutronPortRequest(List<NeutronPort> bulk) {
bulkRequest = bulk;
singletonPort = null;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.codehaus.enunciate.jaxrs.StatusCodes;
return o.extractFields(fields);
}
+ @Context
+ UriInfo uriInfo;
+
/**
* Returns a list of all Ports */
@QueryParam("device_id") String queryDeviceID,
@QueryParam("device_owner") String queryDeviceOwner,
@QueryParam("tenant_id") String queryTenantID,
- // pagination
- @QueryParam("limit") String limit,
+ // linkTitle
+ @QueryParam("limit") Integer limit,
@QueryParam("marker") String marker,
- @QueryParam("page_reverse") String pageReverse
+ @DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
// sorting not supported
) {
INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this);
}
}
}
- //TODO: apply pagination to results
+
+ if (limit != null && ans.size() > 1) {
+ // Return a paginated request
+ NeutronPortRequest request = (NeutronPortRequest) PaginatedRequestFactory.createRequest(limit,
+ marker, pageReverse, uriInfo, ans, NeutronPort.class);
+ return Response.status(200).entity(request).build();
+ }
+
return Response.status(200).entity(
new NeutronPortRequest(ans)).build();
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
-public class NeutronSubnetRequest {
+public class NeutronSubnetRequest implements INeutronRequest {
// See OpenStack Network API v2.0 Reference for description of
// annotated attributes
@XmlElement(name="subnets")
List<NeutronSubnet> bulkRequest;
+ @XmlElement(name="subnets_links")
+ List<NeutronPageLink> links;
+
NeutronSubnetRequest() {
}
+ public NeutronSubnetRequest(List<NeutronSubnet> bulkRequest, List<NeutronPageLink> links) {
+ this.bulkRequest = bulkRequest;
+ this.links = links;
+ this.singletonSubnet = null;
+ }
+
NeutronSubnetRequest(List<NeutronSubnet> bulk) {
bulkRequest = bulk;
singletonSubnet = null;
+ links = null;
}
NeutronSubnetRequest(NeutronSubnet subnet) {
singletonSubnet = subnet;
+ bulkRequest = null;
+ links = null;
}
public NeutronSubnet getSingleton() {
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.codehaus.enunciate.jaxrs.StatusCodes;
return o.extractFields(fields);
}
+ @Context
+ UriInfo uriInfo;
/**
* Returns a list of all Subnets */
@QueryParam("tenant_id") String queryTenantID,
@QueryParam("ipv6_address_mode") String queryIpV6AddressMode,
@QueryParam("ipv6_ra_mode") String queryIpV6RaMode,
- // pagination
- @QueryParam("limit") String limit,
+ // linkTitle
+ @QueryParam("limit") Integer limit,
@QueryParam("marker") String marker,
- @QueryParam("page_reverse") String pageReverse
+ @DefaultValue("false") @QueryParam("page_reverse") Boolean pageReverse
// sorting not supported
) {
- INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD( this);
+ INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this);
if (subnetInterface == null) {
throw new ServiceUnavailableException("Subnet CRUD Interface "
+ RestMessages.SERVICEUNAVAILABLE.toString());
}
}
}
- //TODO: apply pagination to results
+
+ if (limit != null && ans.size() > 1) {
+ // Return a paginated request
+ NeutronSubnetRequest request = (NeutronSubnetRequest) PaginatedRequestFactory.createRequest(limit,
+ marker, pageReverse, uriInfo, ans, NeutronSubnet.class);
+ return Response.status(200).entity(request).build();
+ }
+
return Response.status(200).entity(
new NeutronSubnetRequest(ans)).build();
}
+++ /dev/null
-/*
- * Copyright (C) 2014 Hewlett-Packard Development Company L.P
- *
- * 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
- *
- * Authors : Dave Tucker
- */
-
-package org.opendaylight.controller.networkconfig.neutron.northbound;
-
-import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import java.util.List;
-
-@XmlRootElement
-@XmlAccessorType(XmlAccessType.NONE)
-
-public class PaginatedNeutronNetworkRequest {
-
- @XmlElement (name="networks")
- List<NeutronNetwork> networks;
-
- @XmlElement (name="network_links")
- List<NeutronPageLink> networkLinks;
-
- public PaginatedNeutronNetworkRequest() {
- }
-
- public PaginatedNeutronNetworkRequest(List<NeutronNetwork> networks, List<NeutronPageLink> networkLinks) {
- this.networks = networks;
- this.networkLinks = networkLinks;
- }
-
- public List<NeutronNetwork> getNetworks() {
- return networks;
- }
-
- public void setNetworks(List<NeutronNetwork> networks) {
- this.networks = networks;
- }
-
- public List<NeutronPageLink> getNetworkLinks() {
- return networkLinks;
- }
-
- public void setNetworkLinks(List<NeutronPageLink> networkLinks) {
- this.networkLinks = networkLinks;
- }
-}
--- /dev/null
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * 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
+ *
+ * Authors : Dave Tucker
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.northbound;
+
+import org.opendaylight.controller.networkconfig.neutron.INeutronObject;
+import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork;
+import org.opendaylight.controller.networkconfig.neutron.NeutronPort;
+import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
+
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class PaginatedRequestFactory {
+
+ public static class PaginationResults<T extends INeutronObject> {
+ List<T> collection;
+ List<NeutronPageLink> links;
+
+ public PaginationResults(List<T> collection, List<NeutronPageLink> links) {
+ this.collection = collection;
+ this.links = links;
+ }
+ }
+
+ public static <T extends INeutronObject> INeutronRequest createRequest(Integer limit, String marker,
+ Boolean pageReverse,
+ UriInfo uriInfo,
+ List<T> collection,
+ Class<T> clazz) {
+ PaginationResults results = _paginate(limit, marker, pageReverse, uriInfo, collection);
+
+ if (clazz.equals(NeutronNetwork.class)){
+ return new NeutronNetworkRequest(results.collection, results.links);
+ }
+ if (clazz.equals(NeutronSubnet.class)){
+ return new NeutronSubnetRequest(results.collection, results.links);
+ }
+ if (clazz.equals(NeutronPort.class)){
+ return new NeutronPortRequest(results.collection, results.links);
+ }
+ return null;
+ }
+
+ private static <T extends INeutronObject> PaginationResults _paginate(Integer limit, String marker, Boolean pageReverse, UriInfo uriInfo, List<T> collection) {
+ List<NeutronPageLink> links = new ArrayList<>();
+ Integer startPos = null;
+ String startMarker;
+ String endMarker;
+ Boolean firstPage = false;
+ Boolean lastPage = false;
+
+ Comparator<INeutronObject> neutronObjectComparator = new Comparator<INeutronObject>() {
+ @Override
+ public int compare(INeutronObject o1, INeutronObject o2) {
+ return o1.getID().compareTo(o2.getID());
+ }
+ };
+
+ Collections.sort(collection, neutronObjectComparator);
+
+ if (marker == null) {
+ startPos = 0;
+ }
+
+ else {
+
+ class MarkerObject implements INeutronObject {
+ private String id;
+
+ public String getID() {
+ return id;
+ }
+
+ public void setID(String id) {
+ this.id = id;
+ }
+ }
+
+ INeutronObject markerObject = new MarkerObject();
+
+ markerObject.setID(marker);
+
+ startPos = Collections.binarySearch(collection, markerObject, neutronObjectComparator);
+
+ if (!pageReverse){
+ startPos = startPos + 1;
+ }
+ else {
+ startPos = startPos - limit;
+ }
+
+ }
+
+ if (startPos == null) {
+ throw new ResourceNotFoundException("UUID for marker:" + marker + " could not be found");
+ }
+
+ if (startPos == 0){
+ firstPage = true;
+ }
+
+ if (startPos + limit >= collection.size()) {
+ collection = collection.subList(startPos, collection.size());
+ startMarker = collection.get(0).getID();
+ endMarker = collection.get(collection.size() - 1).getID();
+ lastPage = true;
+ }
+ else if (startPos < 0) {
+ if (startPos + limit > 0) {
+ collection = collection.subList(0, startPos + limit);
+ startMarker = collection.get(0).getID();
+ endMarker = collection.get(collection.size() - 1).getID();
+ firstPage = true;
+ }
+ else {
+ throw new BadRequestException("Requested page is out of bounds. Please check the supplied limit and marker");
+ }
+ }
+ else {
+ collection = collection.subList(startPos, startPos + limit);
+ startMarker = collection.get(0).getID();
+ endMarker = collection.get(limit-1).getID();
+ }
+
+ if (!lastPage) {
+ NeutronPageLink next = new NeutronPageLink();
+ next.setRef("next");
+ next.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + endMarker);
+ links.add(next);
+ }
+
+ if (!firstPage) {
+ NeutronPageLink previous = new NeutronPageLink();
+ previous.setRef("previous");
+ previous.setHref(uriInfo.getAbsolutePath().toString() + "?limit=" + limit.toString() + "&marker=" + startMarker + "&page_reverse=True");
+ links.add(previous);
+ }
+
+ return new PaginationResults(collection, links);
+ }
+}