Merge "Bug 1073: Introduced Transaction Chain to DOMStore APIs."
authorEd Warnicke <eaw@cisco.com>
Fri, 30 May 2014 12:25:16 +0000 (12:25 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 30 May 2014 12:25:16 +0000 (12:25 +0000)
149 files changed:
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryBuilder.java
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractYangTest.java
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeRegistratorTest.java
opendaylight/config/yang-jmx-generator/src/test/resources/config-bgp-listener-impl.yang
opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang
opendaylight/config/yang-test/src/main/yang/config-test-impl.yang
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.xtend [deleted file]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/DataPacketAdapter.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/DataPacketAdapter.xtend [deleted file]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.xtend [deleted file]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.xtend [deleted file]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/SalCompatibilityProvider.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyAdapter.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyAdapter.xtend [deleted file]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyMapping.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyMapping.xtend [deleted file]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.java [new file with mode: 0644]
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.xtend [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.xtend [deleted file]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/YangtoolsMappingHelper.java
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-dom-broker/pom.xml
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataPreconditionFailedException.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataTreeCandidate.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataTreeCandidateNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/DataTreeModification.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ListenerTree.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/ModificationType.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreTreeNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/StoreUtils.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/TreeNodeUtils.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/AlwaysFailOperation.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/InMemoryDataTreeModification.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/ModificationApplyOperation.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/NodeModification.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/NoopDataTreeCandidate.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/NormalizedNodeContainerModificationStrategy.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/OperationWithModification.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/SchemaAwareApplyOperation.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/ValueNodeModificationStrategy.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/spi/AbstractTreeNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/spi/ContainerNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/spi/MutableTreeNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/spi/TreeNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/spi/TreeNodeFactory.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/spi/ValueNode.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/RpcRegistrationWrapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStoreTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.xtend [deleted file]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceActivator.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangDataOperations.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangDataOperations.xtend [deleted file]
opendaylight/md-sal/sal-dom-it/pom.xml [deleted file]
opendaylight/md-sal/sal-rest-connector/pom.xml
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/Draft02.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonToCompositeNodeProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfApplication.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToJsonProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlToCompositeNodeProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ResponseException.java [deleted file]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfError.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/rpc/impl/AbstractRpcExecutor.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/rpc/impl/BrokerRpcExecutor.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/rpc/impl/MountPointRpcExecutor.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/cnsn/test/JsonToCnSnTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/NormalizeNodeTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java
opendaylight/md-sal/sal-schema-repository-api/.gitignore [deleted file]
opendaylight/md-sal/sal-schema-repository-api/pom.xml [deleted file]
opendaylight/md-sal/sal-schema-repository-api/src/main/java/org/opendaylight/controller/sal/schema/api/package-info.java [deleted file]
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java
opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlUnitUtil.java
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_setUnions.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml
opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/Activator.java
opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/NeutronSecurityGroupInterface.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/NeutronSecurityRuleInterface.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronCRUDInterfaces.java
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronPort.java
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSecurityGroup.java [new file with mode: 0644]
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSecurityRule.java [new file with mode: 0644]
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronNorthboundRSApplication.java
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityGroupRequest.java [new file with mode: 0644]
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityGroupsNorthbound.java [new file with mode: 0644]
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityRuleRequest.java [new file with mode: 0644]
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityRulesNorthbound.java [new file with mode: 0644]

index e1e3164f71820d89390431e177e3a75057ea4e8b..c166f668ccd48e9419e79536c7b39523b7f04238 100644 (file)
@@ -49,6 +49,7 @@
     <containermanager.version>0.5.2-SNAPSHOT</containermanager.version>
     <controllermanager.northbound.version>0.0.2-SNAPSHOT</controllermanager.northbound.version>
     <corsfilter.version>7.0.42</corsfilter.version>
+    <ctrie.version>0.2.0</ctrie.version>
     <devices.web.version>0.4.2-SNAPSHOT</devices.web.version>
     <eclipse.persistence.version>2.5.0</eclipse.persistence.version>
     <!-- enforcer version -->
         <artifactId>jackson-module-jaxb-annotations</artifactId>
         <version>${jackson.version}</version>
       </dependency>
+      <dependency>
+        <groupId>com.github.romix</groupId>
+        <artifactId>java-concurrent-hash-trie-map</artifactId>
+        <version>${ctrie.version}</version>
+      </dependency>
       <dependency>
         <groupId>com.google.code.findbugs</groupId>
         <artifactId>jsr305</artifactId>
         <artifactId>guava</artifactId>
         <version>${guava.version}</version>
       </dependency>
+
       <dependency>
         <groupId>com.sun.jersey</groupId>
         <artifactId>jersey-client</artifactId>
         <artifactId>restconf-client-impl</artifactId>
         <version>${yangtools.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>util</artifactId>
+        <version>${yangtools.version}</version>
+      </dependency>
 
       <!-- yangtools dependencies -->
       <dependency>
index 0cc950af65d919ddeeef35029e8f45b6bc10157e..773025c3cf85560ac1c3900d50170155a4df5069 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.controller.config.yangjmxgenerator;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
-
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -73,6 +72,7 @@ public class ModuleMXBeanEntry extends AbstractEntry {
     private final Map<String, QName> providedServices;
 
     private Collection<RuntimeBeanEntry> runtimeBeans;
+    private String nullableDummyContainerName;
 
     ModuleMXBeanEntry(ModuleMXBeanEntryInitial initials, Map<String, AttributeIfc> yangToAttributes,
             Map<String, QName> providedServices2, Collection<RuntimeBeanEntry> runtimeBeans) {
@@ -183,6 +183,15 @@ public class ModuleMXBeanEntry extends AbstractEntry {
                 + '\'' + '}';
     }
 
+    public String getNullableDummyContainerName() {
+        return nullableDummyContainerName;
+    }
+
+    public void setNullableDummyContainerName(String nullableDummyContainerName) {
+        this.nullableDummyContainerName = nullableDummyContainerName;
+    }
+
+
     static final class ModuleMXBeanEntryInitial {
 
         private String localName;
index 676c8eca6ec6f9a903c34aeebd69d9c8abbeae07..6da8dfc663b9cb1e8b8ef43a384151af7c7d18f0 100644 (file)
@@ -7,12 +7,27 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+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;
@@ -35,26 +50,13 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.annotation.Nullable;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.lang.String.format;
-import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName;
-
 final class ModuleMXBeanEntryBuilder {
 
     private Module currentModule;
@@ -233,7 +235,7 @@ final class ModuleMXBeanEntryBuilder {
         return true;
     }
 
-    private void processChoiceCaseNode(Map<String, ModuleMXBeanEntry> result,
+    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,
@@ -266,15 +268,17 @@ final class ModuleMXBeanEntryBuilder {
         // runtime-data
         Collection<RuntimeBeanEntry> runtimeBeans = null;
 
+        HAS_CHILDREN_AND_QNAME dataNodeContainer = getDataNodeContainer(choiceCaseNode);
+
         if (expectedConfigurationAugmentationSchemaPath.equals(augmentation.getTargetPath())) {
             logger.debug("Parsing configuration of {}", moduleLocalNameFromXPath);
-            yangToAttributes = fillConfiguration(choiceCaseNode, currentModule, typeProviderWrapper, qNamesToSIEs,
+            yangToAttributes = fillConfiguration(dataNodeContainer, currentModule, typeProviderWrapper, qNamesToSIEs,
                     schemaContext, packageName);
             checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(), yangToAttributes);
         } else if (expectedStateAugmentationSchemaPath.equals(augmentation.getTargetPath())) {
             logger.debug("Parsing state of {}", moduleLocalNameFromXPath);
             try {
-                runtimeBeans = fillRuntimeBeans(choiceCaseNode, currentModule, typeProviderWrapper, packageName,
+                runtimeBeans = fillRuntimeBeans(dataNodeContainer, currentModule, typeProviderWrapper, packageName,
                         moduleLocalNameFromXPath, javaNamePrefix);
             } catch (NameConflictException e) {
                 throw new NameConflictException(e.getConflictingName(), when.getQName(), when.getQName());
@@ -289,14 +293,20 @@ final class ModuleMXBeanEntryBuilder {
         } else {
             throw new IllegalArgumentException("Cannot parse augmentation " + augmentation);
         }
+        boolean hasDummyContainer = choiceCaseNode.equals(dataNodeContainer) == false;
+
+        String nullableDummyContainerName = hasDummyContainer ? dataNodeContainer.getQName().getLocalName() : null;
         if (result.containsKey(moduleLocalNameFromXPath)) {
-            // either fill runtimeBeans or yangToAttributes
+            // either fill runtimeBeans or yangToAttributes, merge
             ModuleMXBeanEntry moduleMXBeanEntry = result.get(moduleLocalNameFromXPath);
             if (yangToAttributes != null && moduleMXBeanEntry.getAttributes() == null) {
                 moduleMXBeanEntry.setYangToAttributes(yangToAttributes);
             } else if (runtimeBeans != null && moduleMXBeanEntry.getRuntimeBeans() == null) {
                 moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
             }
+            checkState(Objects.equals(nullableDummyContainerName, moduleMXBeanEntry.getNullableDummyContainerName()),
+                    "Mismatch in module " + moduleMXBeanEntry.toString() + " - dummy container must be present/missing in" +
+                            " both state and configuration");
         } else {
             ModuleMXBeanEntry.ModuleMXBeanEntryInitial initial = new ModuleMXBeanEntry.ModuleMXBeanEntryInitialBuilder()
                     .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix)
@@ -309,6 +319,7 @@ final class ModuleMXBeanEntryBuilder {
 
             moduleMXBeanEntry.setYangModuleName(currentModule.getName());
             moduleMXBeanEntry.setYangModuleLocalname(moduleLocalNameFromXPath);
+            moduleMXBeanEntry.setNullableDummyContainerName(nullableDummyContainerName);
             result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
         }
     }
@@ -352,29 +363,52 @@ final class ModuleMXBeanEntryBuilder {
     }
 
     private void checkUniqueTOAttr(Map<String, QName> uniqueGeneratedClassNames, QName parentQName, TOAttribute attr) {
-        final String upperCaseCammelCase = attr.getUpperCaseCammelCase();
-        if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) {
-            QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCammelCase);
-            throw new NameConflictException(upperCaseCammelCase, firstDefinedQName, parentQName);
+        final String upperCaseCamelCase = attr.getUpperCaseCammelCase();
+        if (uniqueGeneratedClassNames.containsKey(upperCaseCamelCase)) {
+            QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCamelCase);
+            throw new NameConflictException(upperCaseCamelCase, firstDefinedQName, parentQName);
         } else {
-            uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName);
+            uniqueGeneratedClassNames.put(upperCaseCamelCase, parentQName);
         }
     }
 
-    private Collection<RuntimeBeanEntry> fillRuntimeBeans(ChoiceCaseNode choiceCaseNode, Module currentModule,
+    private Collection<RuntimeBeanEntry> fillRuntimeBeans(DataNodeContainer dataNodeContainer, Module currentModule,
             TypeProviderWrapper typeProviderWrapper, String packageName, String moduleLocalNameFromXPath,
             String javaNamePrefix) {
 
-        return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, choiceCaseNode, moduleLocalNameFromXPath,
+        return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, dataNodeContainer, moduleLocalNameFromXPath,
                 typeProviderWrapper, javaNamePrefix, currentModule).values();
 
     }
 
-    private Map<String, AttributeIfc> fillConfiguration(ChoiceCaseNode choiceCaseNode, Module currentModule,
+    /**
+     * Since each case statement within a module must provide unique child nodes, it is allowed to wrap
+     * the actual configuration with a container node with name equal to case name.
+     *
+     * @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) {
+        Set<DataSchemaNode> childNodes = choiceCaseNode.getChildNodes();
+        if (childNodes.size() == 1) {
+            DataSchemaNode onlyChild = childNodes.iterator().next();
+            if (onlyChild instanceof ContainerSchemaNode) {
+                ContainerSchemaNode onlyContainer = (ContainerSchemaNode) onlyChild;
+                if (Objects.equals(onlyContainer.getQName().getLocalName(), choiceCaseNode.getQName().getLocalName())) {
+                    // the actual configuration is inside dummy container
+                    return (HAS_CHILDREN_AND_QNAME) onlyContainer;
+                }
+            }
+        }
+        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) {
         Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
-        for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) {
+        Set<DataSchemaNode> childNodes = dataNodeContainer.getChildNodes();
+        for (DataSchemaNode attrNode : childNodes) {
             AttributeIfc attributeValue = getAttributeValue(attrNode, currentModule, qNamesToSIEs, typeProviderWrapper,
                     schemaContext, packageName);
             yangToAttributes.put(attributeValue.getAttributeYangName(), attributeValue);
@@ -483,7 +517,7 @@ final class ModuleMXBeanEntryBuilder {
             String prefix = m.group(1);
             ModuleImport moduleImport = findModuleImport(currentModule, prefix);
             foundModule = schemaContext.findModuleByName(moduleImport.getModuleName(), moduleImport.getRevision());
-            checkState(foundModule != null, format("Module not found in SchemaContext by %s", moduleImport));
+            checkNotNull(foundModule, format("Module not found in SchemaContext by %s", moduleImport));
             localSIName = m.group(2);
         } else {
             foundModule = currentModule; // no prefix => SIE is in currentModule
index cd14458b0fe6b44b76bb1e58513628f27ad8a80c..c941d1504ddae409a9fdfa0c14ec4da957d1038e 100644 (file)
@@ -7,9 +7,24 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
 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.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+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;
@@ -18,7 +33,6 @@ import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribu
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -32,22 +46,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
 /**
  * Holds information about runtime bean to be generated. There are two kinds of
  * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
@@ -68,7 +66,7 @@ public class RuntimeBeanEntry {
 
     @VisibleForTesting
     RuntimeBeanEntry(String packageName,
-            DataSchemaNode nodeForReporting, String yangName,
+            DataNodeContainer nodeForReporting, String yangName,
             String javaNamePrefix, boolean isRoot,
             Optional<String> keyYangName, List<AttributeIfc> attributes,
             List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
@@ -112,7 +110,7 @@ public class RuntimeBeanEntry {
      *         not contain special configuration for it.
      */
     public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
-            String packageName, ChoiceCaseNode container,
+            String packageName, DataNodeContainer container,
             String moduleYangName, TypeProviderWrapper typeProviderWrapper,
             String javaNamePrefix, Module currentModule) {
 
@@ -390,7 +388,7 @@ public class RuntimeBeanEntry {
     }
 
     private static RuntimeBeanEntry createRoot(String packageName,
-            DataSchemaNode nodeForReporting, String attributeYangName,
+            DataNodeContainer nodeForReporting, String attributeYangName,
             List<AttributeIfc> attributes, String javaNamePrefix,
             List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
         return new RuntimeBeanEntry(packageName, nodeForReporting,
index 76d97703af3722142bf361db63960fac77364d3d..b9f2ba9cd1012c2d861212d9fc50ec02b32d051e 100644 (file)
@@ -7,8 +7,19 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator;
 
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.format;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Sets;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.junit.Assert;
 import org.junit.Before;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.YangModelSearchUtils;
@@ -19,18 +30,6 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.format;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
 public abstract class AbstractYangTest {
     protected SchemaContext context;
     protected Map<String, Module> namesToModules; // are module names globally
index 6dd64441df243c87a90610506211bf4c63104e6c..7765b28bb5c5dc621a768c23fdfc58c16d6f9de8 100644 (file)
@@ -11,27 +11,25 @@ import static org.apache.commons.lang3.StringUtils.capitalize;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import com.google.common.base.Optional;
 import java.net.URI;
 import java.util.Collections;
 import java.util.List;
-
 import org.junit.Test;
 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 
-import com.google.common.base.Optional;
-
 public class RuntimeRegistratorTest {
     // TODO add more tests
     protected RuntimeBeanEntry prepareRootRB(List<RuntimeBeanEntry> children) {
 
-        DataSchemaNode dataSchemaNodeForReporting = mock(DataSchemaNode.class);
-        doReturn("DataSchemaNode").when(dataSchemaNodeForReporting).toString();
-        return new RuntimeBeanEntry("pa.cka.ge", dataSchemaNodeForReporting,
+        DataNodeContainer nodeContainer = mock(DataNodeContainer.class);
+        doReturn("DataSchemaNode").when(nodeContainer).toString();
+        return new RuntimeBeanEntry("pa.cka.ge", nodeContainer,
                 "module-name", "ModuleName", true, Optional.<String> absent(),
                 Collections.<AttributeIfc> emptyList(), children,
                 Collections.<Rpc> emptySet());
@@ -39,9 +37,9 @@ public class RuntimeRegistratorTest {
 
     protected RuntimeBeanEntry prepareChildRB(List<RuntimeBeanEntry> children,
             String prefix) {
-        DataSchemaNode dataSchemaNodeForReporting = mock(DataSchemaNode.class);
-        doReturn("DataSchemaNode").when(dataSchemaNodeForReporting).toString();
-        return new RuntimeBeanEntry("pa.cka.ge", dataSchemaNodeForReporting,
+        DataNodeContainer nodeContainer = mock(DataNodeContainer.class);
+        doReturn("DataSchemaNode").when(nodeContainer).toString();
+        return new RuntimeBeanEntry("pa.cka.ge", nodeContainer,
                 prefix + "child-name", capitalize(prefix) + "ChildName", false,
                 Optional.<String> absent(),
                 Collections.<AttributeIfc> emptyList(), children,
index fca83fd6be0e01ce4f7ac26f7001e8fe5d637e9b..0a011b772caa4b3dcc63eb22468c86518be66763 100644 (file)
@@ -23,20 +23,22 @@ module config-bgp-listener-impl {
     augment "/config:modules/config:module/config:state" {
         case bgp-listener-impl {
             when "/config:modules/config:module/config:type = 'bgp-listener-impl'";
-            list peers {
-                config:inner-state-bean;
-                leaf port {
-                    type inet:port-number;
-                    default 179;
+            container bgp-listener-impl {
+                list peers {
+                    config:inner-state-bean;
+                    leaf port {
+                        type inet:port-number;
+                        default 179;
+                    }
+                    leaf core-size {
+                        type uint32;
+                    }
                 }
-                leaf core-size {
-                    type uint32;
-                }
-            }
 
-           leaf as-number {
-                mandatory true;
-                type inet:as-number;
+                leaf as-number {
+                    mandatory true;
+                    type inet:as-number;
+                }
             }
         }
     }
index 4af5b9569a445dfba4d601d18c7f76bf0f64ba0a..e189ef7d66c65b8bdfe9f905889797d84dbb5e36 100644 (file)
@@ -192,42 +192,44 @@ module config-threads-java {
     augment "/config:modules/config:module/config:configuration" {
         case threadpool-dynamic {
             when "/config:modules/config:module/config:type = 'threadpool-dynamic'";
-            leaf core-size {
-                type uint32;
-            }
+            container threadpool-dynamic {
+                leaf core-size {
+                    type uint32;
+                }
 
-            leaf keep-alive {
-                type uint32;
-                units seconds;
-                default 10;
-            }
+                leaf keep-alive {
+                    type uint32;
+                    units seconds;
+                    default 10;
+                }
 
-            leaf maximum-size {
-                type uint32;
-                description "maximum-size description";
-            }
+                leaf maximum-size {
+                    type uint32;
+                    description "maximum-size description";
+                }
 
-            leaf binary {
-                type binary;
-            }
+                leaf binary {
+                    type binary;
+                }
 
-            container threadfactory {
-                description "threadfactory description";
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity th2:threadfactory;
+                container threadfactory {
+                    description "threadfactory description";
+                    uses config:service-ref {
+                        refine type {
+                            mandatory true;
+                            config:required-identity th2:threadfactory;
+                        }
                     }
                 }
-            }
 
-            leaf-list users  {
-                type string;
-            }
+                leaf-list users  {
+                    type string;
+                }
 
-             leaf-list users-numbers {
-                type uint32;
-                description "numbers of users description";
+                 leaf-list users-numbers {
+                    type uint32;
+                    description "numbers of users description";
+                }
             }
         }
     }
@@ -235,9 +237,11 @@ module config-threads-java {
     augment "/config:modules/config:module/config:state" {
         case threadpool-dynamic {
             when "/config:modules/config:module/config:type = 'threadpool-dynamic'";
-            // root runtime bean
-            leaf created-sessions {
-                type uint32;
+            container threadpool-dynamic {
+                // root runtime bean
+                leaf created-sessions {
+                    type uint32;
+                }
             }
         }
     }
index c82da58c150ff8067c0d9933b50250228eec0431..4b006bc72e52c3026130db4b556e62fd340523da 100644 (file)
@@ -162,177 +162,179 @@ module config-test-impl {
     augment "/config:modules/config:module/config:configuration" {
         case impl-netconf {
             when "/config:modules/config:module/config:type = 'impl-netconf'";
-            leaf binaryLeaf {
-                type binary;
-                default ZGVmYXVsdEJpbg==;
-            }
-
-            leaf type {
-                type string;
-                default "default-string";
-            }
+            container impl-netconf {
+                leaf binaryLeaf {
+                    type binary;
+                    default ZGVmYXVsdEJpbg==;
+                }
 
-            leaf extended {
-                type tt:extend-once;
-                default 1;
-            }
+                leaf type {
+                    type string;
+                    default "default-string";
+                }
 
-            leaf extended-twice {
-                type tt:extend-twice;
-                default 2;
-            }
+                leaf extended {
+                    type tt:extend-once;
+                    default 1;
+                }
 
-            leaf extended-enum {
-                type tt:extend-enum;
-                default ONE;
-            }
+                leaf extended-twice {
+                    type tt:extend-twice;
+                    default 2;
+                }
 
-            leaf ip {
-                type inet:ip-address;
-                default 0:0:0:0:0:0:0:1;
-            }
+                leaf extended-enum {
+                    type tt:extend-enum;
+                    default ONE;
+                }
 
-            leaf union-test-attr {
-                type tt:unionTest;
-                default 456;
-            }
+                leaf ip {
+                    type inet:ip-address;
+                    default 0:0:0:0:0:0:0:1;
+                }
 
-            leaf sleep-factor {
-                type decimal64 {
-                    fraction-digits 2;
+                leaf union-test-attr {
+                    type tt:unionTest;
+                    default 456;
                 }
-                default 2.00;
-            }
 
-           container dto-c {
-                leaf simple-arg {
-                    type uint32;
+                leaf sleep-factor {
+                    type decimal64 {
+                        fraction-digits 2;
+                    }
+                    default 2.00;
                 }
 
-                container dto-a-inner {
+               container dto-c {
                     leaf simple-arg {
                         type uint32;
                     }
 
-                    container dto-a-inner-inner {
+                    container dto-a-inner {
                         leaf simple-arg {
                             type uint32;
                         }
+
+                        container dto-a-inner-inner {
+                            leaf simple-arg {
+                                type uint32;
+                            }
+                        }
                     }
                 }
-            }
-
-            leaf simpleInt {
-                type uint32;
-            }
-
-            leaf simpleBoolean {
-                type boolean;
-                default false;
-            }
-
-            leaf simple-long {
-                type int64;
-                default -45;
-            }
-
-            leaf simple-long-2 {
-                type uint32;
-                default 445;
-            }
 
-            leaf simple-BigInteger {
-                type uint64;
-                default 545454;
-            }
+                leaf simpleInt {
+                    type uint32;
+                }
 
-            leaf simple-byte {
-                type int8;
-                default -4;
-            }
+                leaf simpleBoolean {
+                    type boolean;
+                    default false;
+                }
 
-            leaf simple-short {
-                type uint8;
-                default 45;
-            }
+                leaf simple-long {
+                    type int64;
+                    default -45;
+                }
 
-            leaf simple-test {
-                type uint16;
-                default 99;
-            }
+                leaf simple-long-2 {
+                    type uint32;
+                    default 445;
+                }
 
-            leaf-list simple-list {
-                type uint16;
-            }
+                leaf simple-BigInteger {
+                    type uint64;
+                    default 545454;
+                }
 
-            container dto_d {
-                leaf simple-int1 {
-                    type uint32;
+                leaf simple-byte {
+                    type int8;
+                    default -4;
                 }
 
-                leaf simple-int2 {
-                    type uint32;
+                leaf simple-short {
+                    type uint8;
+                    default 45;
                 }
 
-                leaf simple-int3 {
+                leaf simple-test {
                     type uint16;
+                    default 99;
                 }
 
                 leaf-list simple-list {
                     type uint16;
                 }
 
-                list complex-dto-bInner {
-                    leaf-list simple-list {
-                        type uint16;
+                container dto_d {
+                    leaf simple-int1 {
+                        type uint32;
                     }
+
+                    leaf simple-int2 {
+                        type uint32;
+                    }
+
                     leaf simple-int3 {
                         type uint16;
                     }
 
-                    container deep {
+                    leaf-list simple-list {
+                        type uint16;
+                    }
+
+                    list complex-dto-bInner {
+                        leaf-list simple-list {
+                            type uint16;
+                        }
                         leaf simple-int3 {
                             type uint16;
-                            default 0;
+                        }
+
+                        container deep {
+                            leaf simple-int3 {
+                                type uint16;
+                                default 0;
+                            }
                         }
                     }
                 }
-            }
 
-            list complex-list {
-                list simple-list {
-                    leaf simple-int3 {
-                        type uint16;
+                list complex-list {
+                    list simple-list {
+                        leaf simple-int3 {
+                            type uint16;
+                        }
                     }
                 }
-            }
 
-            list peers {
-                    leaf port {
-                        type string;
-                    }
-                    leaf core-size {
-                        type uint32;
-                    }
-                    leaf simple-int3 {
-                        type uint16;
-                    }
-            }
+                list peers {
+                        leaf port {
+                            type string;
+                        }
+                        leaf core-size {
+                            type uint32;
+                        }
+                        leaf simple-int3 {
+                            type uint16;
+                        }
+                }
 
-            container testing-dep {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity test:testing;
+                container testing-dep {
+                    uses config:service-ref {
+                        refine type {
+                            mandatory true;
+                            config:required-identity test:testing;
+                        }
                     }
                 }
-            }
 
-            list testing-deps {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity test:testing;
+                list testing-deps {
+                    uses config:service-ref {
+                        refine type {
+                            mandatory true;
+                            config:required-identity test:testing;
+                        }
                     }
                 }
             }
@@ -342,97 +344,99 @@ module config-test-impl {
     augment "/config:modules/config:module/config:state" {
         case impl-netconf {
             when "/config:modules/config:module/config:type = 'impl-netconf'";
-            // rpc
-            rpcx:rpc-context-instance "test-rpc";
-
-            // root runtime bean
-            leaf created-sessions {
-                type uint32;
-            }
-
-            container asdf {
-                leaf simpleInt {
-                    type uint16;
-                }
+            container impl-netconf {
+                // rpc
+                rpcx:rpc-context-instance "test-rpc";
 
-                leaf simpleString {
-                    type string;
+                // root runtime bean
+                leaf created-sessions {
+                    type uint32;
                 }
-            }
 
+                container asdf {
+                    leaf simpleInt {
+                        type uint16;
+                    }
 
-            list inner-running-data-additional {
-                config:inner-state-bean;
+                    leaf simpleString {
+                        type string;
+                    }
+                }
 
-                // rpc
-                rpcx:rpc-context-instance "inner-test-rpc";
 
-                key "simpleString";
+                list inner-running-data-additional {
+                    config:inner-state-bean;
 
-                leaf simple-int3 {
-                        type uint16;
-                }
+                    // rpc
+                    rpcx:rpc-context-instance "inner-test-rpc";
 
-                leaf simpleString {
-                    type string;
-                }
+                    key "simpleString";
 
-                container deep4 {
-                    leaf boool {
-                        type boolean;
+                    leaf simple-int3 {
+                            type uint16;
                     }
-                }
-            }
-
-             list inner-running-data {
-                config:inner-state-bean;
 
-                key "simple-int3";
-
-                leaf simple-int3 {
-                        type uint16;
+                    leaf simpleString {
+                        type string;
                     }
 
-                    container deep2 {
-                    leaf boool {
-                        type boolean;
+                    container deep4 {
+                        leaf boool {
+                            type boolean;
+                        }
                     }
-                 }
+                }
 
-                list inner-inner-running-data {
+                 list inner-running-data {
                     config:inner-state-bean;
 
-                    rpcx:rpc-context-instance "inner-inner-test-rpc";
-                    rpcx:rpc-context-instance "complex-output-rpc";
-
                     key "simple-int3";
 
                     leaf simple-int3 {
-                        type uint16;
-                    }
+                            type uint16;
+                        }
 
-                    leaf-list list-of-strings {
-                        type string;
-                    }
+                        container deep2 {
+                        leaf boool {
+                            type boolean;
+                        }
+                     }
 
-                    list not-state-bean {
-                        leaf element {
-                            type string;
+                    list inner-inner-running-data {
+                        config:inner-state-bean;
+
+                        rpcx:rpc-context-instance "inner-inner-test-rpc";
+                        rpcx:rpc-context-instance "complex-output-rpc";
+
+                        key "simple-int3";
+
+                        leaf simple-int3 {
+                            type uint16;
                         }
 
-                        list not-state-bean-internal {
-                            // This should be ignored
-                            config:inner-state-bean;
+                        leaf-list list-of-strings {
+                            type string;
+                        }
 
-                            leaf element2 {
+                        list not-state-bean {
+                            leaf element {
                                 type string;
                             }
+
+                            list not-state-bean-internal {
+                                // This should be ignored
+                                config:inner-state-bean;
+
+                                leaf element2 {
+                                    type string;
+                                }
+                            }
                         }
-                    }
 
-                    container deep3 {
-                        leaf boool {
-                            type boolean;
+                        container deep3 {
+                            leaf boool {
+                                type boolean;
+                            }
                         }
                     }
                 }
index 52aa6d276b7337a60bbfc891f32393416cccbd8c..5b44bb7569b9c3df2bce4c6ef76c4741e7541061 100644 (file)
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>restconf-client-impl</artifactId>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.yangtools</groupId>
+          <artifactId>util</artifactId>
+        </dependency>
         <!-- yangtools dependencies I'm pretty sure we can trim -->
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
index c958039b5f0465206626eb009e33385df5b11d2f..f15f8f7404d1420d0efc361551c99b0355d60170 100644 (file)
@@ -13,11 +13,13 @@ osgi.bundles=\
     reference\:file\:../lib/jersey-server-1.17.jar@2:start
 
 # Netconf startup configuration
-netconf.tcp.address=127.0.0.1
-netconf.tcp.port=8383
 
-netconf.tcp.client.address=127.0.0.1
-netconf.tcp.client.port=8383
+# Netconf tcp address:port is optional with default value 127.0.0.1:8383
+#netconf.tcp.address=127.0.0.1
+#netconf.tcp.port=8384
+
+#netconf.tcp.client.address=127.0.0.1
+#netconf.tcp.client.port=8384
 
 netconf.ssh.address=0.0.0.0
 netconf.ssh.port=1830
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.java
new file mode 100644 (file)
index 0000000..ba68d52
--- /dev/null
@@ -0,0 +1,239 @@
+/**
+ * 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.compatibility;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dm.Component;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.compatibility.adsal.DataPacketServiceAdapter;
+import org.opendaylight.controller.sal.compatibility.topology.TopologyAdapter;
+import org.opendaylight.controller.sal.compatibility.topology.TopologyProvider;
+import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
+import org.opendaylight.controller.sal.core.NodeConnector.NodeConnectorIDType;
+import org.opendaylight.controller.sal.discovery.IDiscoveryService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
+import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
+import org.opendaylight.controller.sal.packet.IPluginInDataPacketService;
+import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService;
+import org.opendaylight.controller.sal.reader.IPluginInReadService;
+import org.opendaylight.controller.sal.reader.IPluginOutReadService;
+import org.opendaylight.controller.sal.topology.IPluginInTopologyService;
+import org.opendaylight.controller.sal.topology.IPluginOutTopologyService;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.INodeConnectorFactory;
+import org.opendaylight.controller.sal.utils.INodeFactory;
+import org.osgi.framework.BundleContext;
+
+import com.google.common.base.Preconditions;
+
+public class ComponentActivator extends ComponentActivatorAbstractBase {
+    private final INodeConnectorFactory nodeConnectorFactory = new MDSalNodeConnectorFactory();
+    private final DataPacketServiceAdapter dataPacketService = new DataPacketServiceAdapter();
+    private final InventoryAndReadAdapter inventory = new InventoryAndReadAdapter();
+    private final FlowProgrammerAdapter flow = new FlowProgrammerAdapter();
+    private final DataPacketAdapter dataPacket = new DataPacketAdapter();
+    private final TopologyProvider tpProvider = new TopologyProvider();
+    private final INodeFactory nodeFactory = new MDSalNodeFactory();
+    private final TopologyAdapter topology = new TopologyAdapter();
+    private BundleContext context;
+
+    public INodeConnectorFactory getNodeConnectorFactory() {
+        return nodeConnectorFactory;
+    }
+
+    public DataPacketServiceAdapter getDataPacketService() {
+        return dataPacketService;
+    }
+
+    public InventoryAndReadAdapter getInventory() {
+        return inventory;
+    }
+
+    public FlowProgrammerAdapter getFlow() {
+        return flow;
+    }
+
+    public DataPacketAdapter getDataPacket() {
+        return dataPacket;
+    }
+
+    public TopologyProvider getTpProvider() {
+        return tpProvider;
+    }
+
+    public INodeFactory getNodeFactory() {
+        return nodeFactory;
+    }
+
+    public TopologyAdapter getTopology() {
+        return topology;
+    }
+
+    @Override
+    protected void init() {
+        NodeIDType.registerIDType(NodeMapping.MD_SAL_TYPE, String.class);
+        NodeConnectorIDType.registerIDType(NodeMapping.MD_SAL_TYPE, String.class, NodeMapping.MD_SAL_TYPE);
+    }
+
+    @Override
+    public void start(final BundleContext context) {
+        super.start(context);
+        this.context = Preconditions.checkNotNull(context);
+    }
+
+    public ProviderContext setBroker(final BindingAwareBroker broker) {
+        return broker.registerProvider(new SalCompatibilityProvider(this), context);
+    }
+
+    @Override
+    protected Object[] getGlobalImplementations() {
+        return new Object[] {
+                flow,
+                inventory,
+                dataPacket,
+                nodeFactory,
+                nodeConnectorFactory,
+                topology,
+                tpProvider,
+                this // Used for setBroker callback
+        };
+    }
+
+    @Override
+    protected void configureGlobalInstance(final Component c, final Object imp) {
+        if (imp instanceof DataPacketAdapter) {
+            _configure((DataPacketAdapter)imp, c);
+        } else if (imp instanceof FlowProgrammerAdapter) {
+            _configure((FlowProgrammerAdapter)imp, c);
+        } else if (imp instanceof InventoryAndReadAdapter) {
+            _configure((InventoryAndReadAdapter)imp, c);
+        } else if (imp instanceof ComponentActivator) {
+            _configure((ComponentActivator)imp, c);
+        } else if (imp instanceof MDSalNodeConnectorFactory) {
+            _configure((MDSalNodeConnectorFactory)imp, c);
+        } else if (imp instanceof MDSalNodeFactory) {
+            _configure((MDSalNodeFactory)imp, c);
+        } else if (imp instanceof TopologyAdapter) {
+            _configure((TopologyAdapter)imp, c);
+        } else if (imp instanceof TopologyProvider) {
+            _configure((TopologyProvider)imp, c);
+        } else {
+            throw new IllegalArgumentException(String.format("Unhandled implementation class %s", imp.getClass()));
+        }
+    }
+
+    @Override
+    protected Object[] getImplementations() {
+        return new Object[] {
+                dataPacketService,
+        };
+    }
+
+    @Override
+    protected void configureInstance(final Component c, final Object imp, final String containerName) {
+        if (imp instanceof ComponentActivator) {
+            _instanceConfigure((ComponentActivator)imp, c, containerName);
+        } else if (imp instanceof DataPacketServiceAdapter) {
+            _instanceConfigure((DataPacketServiceAdapter)imp, c, containerName);
+        } else {
+            throw new IllegalArgumentException(String.format("Unhandled implementation class %s", imp.getClass()));
+        }
+    }
+
+    private void _configure(final MDSalNodeFactory imp, final Component it) {
+        it.setInterface(INodeFactory.class.getName(), properties());
+    }
+
+    private void _configure(final MDSalNodeConnectorFactory imp, final Component it) {
+        it.setInterface(INodeConnectorFactory.class.getName(), properties());
+    }
+
+    private void _configure(final ComponentActivator imp, final Component it) {
+        it.add(createServiceDependency()
+                .setService(BindingAwareBroker.class)
+                .setCallbacks("setBroker", "setBroker")
+                .setRequired(true));
+    }
+
+    private void _configure(final DataPacketAdapter imp, final Component it) {
+        it.add(createServiceDependency()
+                .setService(IPluginOutDataPacketService.class)
+                .setCallbacks("setDataPacketPublisher", "setDataPacketPublisher")
+                .setRequired(false));
+    }
+
+    private void _configure(final FlowProgrammerAdapter imp, final Component it) {
+        it.setInterface(IPluginInFlowProgrammerService.class.getName(), properties());
+        it.add(createServiceDependency()
+                .setService(IPluginOutFlowProgrammerService.class)
+                .setCallbacks("setFlowProgrammerPublisher", "setFlowProgrammerPublisher")
+                .setRequired(false));
+        it.add(createServiceDependency()
+                .setService(IClusterGlobalServices.class)
+                .setCallbacks("setClusterGlobalServices", "unsetClusterGlobalServices")
+                .setRequired(false));
+    }
+
+    private void _instanceConfigure(final DataPacketServiceAdapter imp, final Component it, final String containerName) {
+        it.setInterface(IPluginInDataPacketService.class.getName(), properties());
+    }
+
+    private void _instanceConfigure(final ComponentActivator imp, final Component it, final String containerName) {
+        // No-op
+    }
+
+    private void _configure(final InventoryAndReadAdapter imp, final Component it) {
+        it.setInterface(new String[] {
+                IPluginInInventoryService.class.getName(),
+                IPluginInReadService.class.getName(),
+        }, properties());
+
+        it.add(createServiceDependency()
+                .setService(IPluginOutReadService.class)
+                .setCallbacks("setReadPublisher", "unsetReadPublisher")
+                .setRequired(false));
+        it.add(createServiceDependency()
+                .setService(IPluginOutInventoryService.class)
+                .setCallbacks("setInventoryPublisher", "unsetInventoryPublisher")
+                .setRequired(false));
+        it.add(createServiceDependency()
+                .setService(IDiscoveryService.class)
+                .setCallbacks("setDiscoveryPublisher", "setDiscoveryPublisher")
+                .setRequired(false));
+    }
+
+    private void _configure(final TopologyAdapter imp, final Component it) {
+        it.setInterface(IPluginInTopologyService.class.getName(), properties());
+
+        it.add(createServiceDependency()
+                .setService(IPluginOutTopologyService.class)
+                .setCallbacks("setTopologyPublisher", "setTopologyPublisher")
+                .setRequired(false));
+    }
+
+    private void _configure(final TopologyProvider imp, final Component it) {
+        it.add(createServiceDependency()
+                .setService(IPluginOutTopologyService.class)
+                .setCallbacks("setTopologyPublisher", "setTopologyPublisher")
+                .setRequired(false));
+    }
+
+    private Dictionary<String,Object> properties() {
+        final Hashtable<String,Object> props = new Hashtable<String, Object>();
+        props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), NodeMapping.MD_SAL_TYPE);
+        props.put("protocolName", NodeMapping.MD_SAL_TYPE);
+        return props;
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ComponentActivator.xtend
deleted file mode 100644 (file)
index 57682bc..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.compatibility
-
-import java.util.Arrays
-import java.util.Dictionary
-import java.util.Hashtable
-import org.apache.felix.dm.Component
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider
-import org.opendaylight.controller.sal.binding.api.NotificationService
-import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService
-import org.opendaylight.controller.sal.compatibility.adsal.DataPacketServiceAdapter
-import org.opendaylight.controller.sal.compatibility.topology.TopologyAdapter
-import org.opendaylight.controller.sal.compatibility.topology.TopologyProvider
-import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase
-import org.opendaylight.controller.sal.core.Node
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.discovery.IDiscoveryService
-import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService
-import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService
-import org.opendaylight.controller.sal.inventory.IPluginInInventoryService
-import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService
-import org.opendaylight.controller.sal.packet.IPluginInDataPacketService
-import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService
-import org.opendaylight.controller.sal.reader.IPluginInReadService
-import org.opendaylight.controller.sal.reader.IPluginOutReadService
-import org.opendaylight.controller.sal.topology.IPluginInTopologyService
-import org.opendaylight.controller.sal.topology.IPluginOutTopologyService
-import org.opendaylight.controller.sal.utils.GlobalConstants
-import org.opendaylight.controller.sal.utils.INodeConnectorFactory
-import org.opendaylight.controller.sal.utils.INodeFactory
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService
-import org.osgi.framework.BundleContext
-
-import static org.opendaylight.controller.sal.compatibility.NodeMapping.*
-
-class ComponentActivator extends ComponentActivatorAbstractBase {
-
-    private BundleContext context;
-
-    @Property
-    FlowProgrammerAdapter flow = new FlowProgrammerAdapter;
-
-    @Property
-    InventoryAndReadAdapter inventory = new InventoryAndReadAdapter;
-
-    @Property
-    DataPacketAdapter dataPacket = new DataPacketAdapter;
-
-    @Property
-    INodeFactory nodeFactory = new MDSalNodeFactory
-
-    @Property
-    INodeConnectorFactory nodeConnectorFactory = new MDSalNodeConnectorFactory
-    
-    @Property
-    TopologyAdapter topology = new TopologyAdapter
-    
-    @Property
-    TopologyProvider tpProvider = new TopologyProvider()
-
-    @Property
-    DataPacketServiceAdapter dataPacketService = new DataPacketServiceAdapter()
-
-
-
-    override protected init() {
-        Node.NodeIDType.registerIDType(MD_SAL_TYPE, String);
-        NodeConnector.NodeConnectorIDType.registerIDType(MD_SAL_TYPE, String, MD_SAL_TYPE);
-    }
-
-    override start(BundleContext context) {
-        super.start(context)
-        this.context = context;
-    }
-
-    def setBroker(BindingAwareBroker broker) {
-        broker.registerProvider(new SalCompatibilityProvider(this), context)
-    }
-
-
-    override protected getGlobalImplementations() {
-        return Arrays.asList(this, flow, inventory, dataPacket, nodeFactory, nodeConnectorFactory,topology,tpProvider)
-    }
-
-    override protected configureGlobalInstance(Component c, Object imp) {
-        configure(imp, c);
-    }
-
-    override protected getImplementations() {
-        return Arrays.asList(dataPacketService)
-    }
-
-    override protected configureInstance(Component c, Object imp, String containerName) {
-        instanceConfigure(imp, c, containerName);
-    }
-
-    private def dispatch configure(MDSalNodeFactory imp, Component it) {
-        setInterface(INodeFactory.name, properties);
-    }
-
-    private def dispatch configure(MDSalNodeConnectorFactory imp, Component it) {
-        setInterface(INodeConnectorFactory.name, properties);
-    }
-
-    private def dispatch configure(ComponentActivator imp, Component it) {
-        add(
-            createServiceDependency().setService(BindingAwareBroker) //
-            .setCallbacks("setBroker", "setBroker") //
-            .setRequired(true))
-
-
-    }
-
-    private def dispatch configure(DataPacketAdapter imp, Component it) {
-        add(
-            createServiceDependency() //
-            .setService(IPluginOutDataPacketService) //
-            .setCallbacks("setDataPacketPublisher", "setDataPacketPublisher") //
-            .setRequired(false))
-    }
-
-    private def dispatch configure(FlowProgrammerAdapter imp, Component it) {
-        setInterface(IPluginInFlowProgrammerService.name, properties)
-        add(
-            createServiceDependency() //
-            .setService(IPluginOutFlowProgrammerService) //
-            .setCallbacks("setFlowProgrammerPublisher", "setFlowProgrammerPublisher") //
-            .setRequired(false))
-
-        add(
-            createServiceDependency() //
-            .setService(IClusterGlobalServices) //
-            .setCallbacks("setClusterGlobalServices", "unsetClusterGlobalServices") //
-            .setRequired(false))
-
-    }
-
-    private def dispatch instanceConfigure(DataPacketServiceAdapter imp, Component it, String containerName) {
-        setInterface(IPluginInDataPacketService.name, properties)
-    }
-
-    private def dispatch instanceConfigure(ComponentActivator imp, Component it, String containerName) {
-    }
-
-
-    private def dispatch configure(InventoryAndReadAdapter imp, Component it) {
-        setInterface(Arrays.asList(IPluginInInventoryService.name, IPluginInReadService.name), properties)
-        add(
-            createServiceDependency() //
-            .setService(IPluginOutReadService) //
-            .setCallbacks("setReadPublisher", "unsetReadPublisher") //
-            .setRequired(false))
-        add(
-            createServiceDependency() //
-            .setService(IPluginOutInventoryService) //
-            .setCallbacks("setInventoryPublisher", "unsetInventoryPublisher") //
-            .setRequired(false))
-        add(
-            createServiceDependency() //
-            .setService(IDiscoveryService) //
-            .setCallbacks("setDiscoveryPublisher", "setDiscoveryPublisher") //
-            .setRequired(false))
-
-        
-    }
-    
-    private def dispatch configure (TopologyAdapter imp, Component it) {
-        setInterface(Arrays.asList(IPluginInTopologyService.name), properties)
-        add(
-            createServiceDependency() //
-            .setService(IPluginOutTopologyService) //
-            .setCallbacks("setTopologyPublisher", "setTopologyPublisher") //
-            .setRequired(false))
-    }
-    
-    private def dispatch configure (TopologyProvider imp, Component it) {
-        add(
-            createServiceDependency() //
-            .setService(IPluginOutTopologyService) //
-            .setCallbacks("setTopologyPublisher", "setTopologyPublisher") //
-            .setRequired(false))
-    }
-
-    private def Dictionary<String, Object> properties() {
-        val props = new Hashtable<String, Object>();
-        props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString, MD_SAL_TYPE)
-        props.put("protocolName", MD_SAL_TYPE);
-        return props;
-    }
-}
-package class SalCompatibilityProvider implements BindingAwareProvider {
-    
-    private val ComponentActivator activator;
-    
-    new(ComponentActivator cmpAct) {
-        activator = cmpAct;
-    }
-    
-    override getFunctionality() {
-        // Noop
-    }
-    
-    override getImplementations() {
-        // Noop
-    }
-    
-    
-    override onSessionInitialized(ConsumerContext session) {
-        // Noop
-    }
-    
-    
-    override onSessionInitiated(ProviderContext session) {
-        val it = activator
-                val subscribe = session.getSALService(NotificationService)
-
-        // Registration of Flow Service
-        flow.delegate = session.getRpcService(SalFlowService)
-        flow.dataBrokerService = session.getSALService(DataBrokerService);
-        subscribe.registerNotificationListener(flow);
-
-        // Data Packet Service
-        subscribe.registerNotificationListener(inventory);
-        dataPacketService.delegate = session.getRpcService(PacketProcessingService)
-
-        // Inventory Service
-        inventory.dataService = session.getSALService(DataBrokerService);
-        inventory.flowStatisticsService = session.getRpcService(OpendaylightFlowStatisticsService);
-        inventory.flowTableStatisticsService = session.getRpcService(OpendaylightFlowTableStatisticsService);
-        inventory.nodeConnectorStatisticsService = session.getRpcService(OpendaylightPortStatisticsService);
-        inventory.topologyDiscovery = session.getRpcService(FlowTopologyDiscoveryService);
-        inventory.dataProviderService = session.getSALService(DataProviderService)
-        topology.dataService = session.getSALService(DataProviderService)
-        tpProvider.dataService = session.getSALService(DataProviderService)
-
-        inventory.startAdapter();
-
-        tpProvider.startAdapter();
-
-        subscribe.registerNotificationListener(dataPacket)
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/DataPacketAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/DataPacketAdapter.java
new file mode 100644 (file)
index 0000000..4d26a3a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.compatibility;
+
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DataPacketAdapter implements PacketProcessingListener {
+    private static final Logger LOG = LoggerFactory.getLogger(DataPacketAdapter.class);
+
+    // These are injected via Apache DM (see ComponentActivator)
+    private IPluginOutDataPacketService dataPacketPublisher;
+
+    @Override
+    public void onPacketReceived(final PacketReceived packet) {
+        try {
+            RawPacket inPacket = toRawPacket(packet);
+            if (dataPacketPublisher != null) {
+                dataPacketPublisher.receiveDataPacket(inPacket);
+            } else {
+                LOG.warn("IPluginOutDataPacketService is not available. Not forwarding packet to AD-SAL.");
+            }
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct raw packet from {}, dropping it", packet, e);
+        }
+    }
+
+    public static RawPacket toRawPacket(final PacketReceived received) throws ConstructionException {
+        final RawPacket ret = new RawPacket(received.getPayload());
+        ret.setIncomingNodeConnector(NodeMapping.toADNodeConnector(received.getIngress()));
+        return ret;
+    }
+
+    public IPluginOutDataPacketService getDataPacketPublisher() {
+        return dataPacketPublisher;
+    }
+
+    // These are injected via Apache DM (see ComponentActivator)
+    public void setDataPacketPublisher(final IPluginOutDataPacketService dataPacketPublisher) {
+        this.dataPacketPublisher = dataPacketPublisher;
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/DataPacketAdapter.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/DataPacketAdapter.xtend
deleted file mode 100644 (file)
index 8581d4a..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.compatibility
-
-import org.opendaylight.controller.sal.packet.IPluginOutDataPacketService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived
-import org.opendaylight.controller.sal.packet.RawPacket
-
-class DataPacketAdapter implements PacketProcessingListener {
-
-    @Property
-    IPluginOutDataPacketService dataPacketPublisher;
-
-    override onPacketReceived(PacketReceived packet) {
-        val RawPacket inPacket = packet.toRawPacket();
-        dataPacketPublisher.receiveDataPacket(inPacket);
-    }
-
-    public static def RawPacket toRawPacket(PacketReceived received) {        
-        val ret = new RawPacket(received.payload);
-        ret.setIncomingNodeConnector(NodeMapping.toADNodeConnector(received.ingress))
-        return ret;
-    }
-
-}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java
new file mode 100644 (file)
index 0000000..e5a9d3e
--- /dev/null
@@ -0,0 +1,302 @@
+/**
+ * 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.compatibility;
+
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices.cacheMode;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FlowProgrammerAdapter implements IPluginInFlowProgrammerService, SalFlowListener {
+    private final static Logger LOG = LoggerFactory.getLogger(FlowProgrammerAdapter.class);
+
+    // Note: clustering services manipulate this
+    private final Map<Flow, UUID> flowToFlowId = new ConcurrentHashMap<Flow, UUID>();
+    private final static String CACHE_NAME = "flowprogrammeradapter.flowtoid";
+
+    // These are injected via Apache DM (see ComponentActivator)
+    private IPluginOutFlowProgrammerService flowProgrammerPublisher;
+    private IClusterGlobalServices clusterGlobalServices;
+    private DataBrokerService dataBrokerService;
+    private SalFlowService delegate;
+
+    public SalFlowService getDelegate() {
+        return this.delegate;
+    }
+
+    public void setDelegate(final SalFlowService delegate) {
+        this.delegate = delegate;
+    }
+
+    public DataBrokerService getDataBrokerService() {
+        return this.dataBrokerService;
+    }
+
+    public void setDataBrokerService(final DataBrokerService dataBrokerService) {
+        this.dataBrokerService = dataBrokerService;
+    }
+
+    public IPluginOutFlowProgrammerService getFlowProgrammerPublisher() {
+        return this.flowProgrammerPublisher;
+    }
+
+    public void setFlowProgrammerPublisher(final IPluginOutFlowProgrammerService flowProgrammerPublisher) {
+        this.flowProgrammerPublisher = flowProgrammerPublisher;
+    }
+
+    public IClusterGlobalServices getClusterGlobalServices() {
+        return this.clusterGlobalServices;
+    }
+
+    public void setClusterGlobalServices(final IClusterGlobalServices clusterGlobalServices) {
+        this.clusterGlobalServices = clusterGlobalServices;
+    }
+
+    @Override
+    public Status addFlow(final Node node, final Flow flow) {
+        return toFutureStatus(internalAddFlowAsync(node, flow, 0));
+    }
+
+    @Override
+    public Status modifyFlow(final Node node, final Flow oldFlow, final Flow newFlow) {
+        return toFutureStatus(internalModifyFlowAsync(node, oldFlow, newFlow, 0));
+    }
+
+    @Override
+    public Status removeFlow(final Node node, final Flow flow) {
+        return toFutureStatus(internalRemoveFlowAsync(node, flow, 0));
+    }
+
+    @Override
+    public Status addFlowAsync(final Node node, final Flow flow, final long rid) {
+        // FIXME is this correct? What if the future fails?
+        this.internalAddFlowAsync(node, flow, rid);
+        return FlowProgrammerAdapter.toStatus(true);
+    }
+
+    @Override
+    public Status modifyFlowAsync(final Node node, final Flow oldFlow, final Flow newFlow, final long rid) {
+        // FIXME is this correct? What if the future fails?
+        this.internalModifyFlowAsync(node, oldFlow, newFlow, rid);
+        return FlowProgrammerAdapter.toStatus(true);
+    }
+
+    @Override
+    public Status removeFlowAsync(final Node node, final Flow flow, final long rid) {
+        // FIXME is this correct? What if the future fails?
+        this.internalRemoveFlowAsync(node, flow, rid);
+        return FlowProgrammerAdapter.toStatus(true);
+    }
+
+    @Override
+    public Status removeAllFlows(final Node node) {
+        // FIXME: unfinished?
+        return new Status(StatusCode.SUCCESS);
+    }
+
+    @Override
+    public Status syncSendBarrierMessage(final Node node) {
+        // FIXME: unfinished?
+        return null;
+    }
+
+    @Override
+    public Status asyncSendBarrierMessage(final Node node) {
+        // FIXME: unfinished?
+        return null;
+    }
+
+    private static Status toStatus(final boolean successful) {
+        return new Status(successful ? StatusCode.SUCCESS : StatusCode.INTERNALERROR);
+    }
+
+    public static Status toStatus(final RpcResult<? extends Object> result) {
+        return toStatus(result.isSuccessful());
+    }
+
+    @Override
+    public void onFlowAdded(final FlowAdded notification) {
+        // FIXME: unfinished?
+    }
+
+    @Override
+    public void onFlowRemoved(final FlowRemoved notification) {
+        if (notification == null) {
+            return;
+        }
+
+        final NodeRef node = notification.getNode();
+        if (node == null) {
+            LOG.debug("Notification {} has not node, ignoring it", notification);
+            return;
+        }
+
+        Node adNode;
+        try {
+            adNode = NodeMapping.toADNode(notification.getNode());
+        } catch (ConstructionException e) {
+            LOG.warn("Failed to construct AD node for {}, ignoring notification", node, e);
+            return;
+        }
+        flowProgrammerPublisher.flowRemoved(adNode, ToSalConversionsUtils.toFlow(notification, adNode));
+    }
+
+    @Override
+    public void onFlowUpdated(final FlowUpdated notification) {
+        // FIXME: unfinished?
+    }
+
+    @Override
+    public void onSwitchFlowRemoved(final SwitchFlowRemoved notification) {
+        // FIXME: unfinished?
+    }
+
+    @Override
+    public void onNodeErrorNotification(final NodeErrorNotification notification) {
+        // FIXME: unfinished?
+    }
+
+    @Override
+    public void onNodeExperimenterErrorNotification(final NodeExperimenterErrorNotification notification) {
+        // FIXME: unfinished?
+    }
+
+    private static final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow> flowPath(
+            final org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow flow, final NodeKey nodeKey) {
+        return InstanceIdentifier.builder(Nodes.class)
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey)
+                .augmentation(FlowCapableNode.class)
+                .child(Table.class, new TableKey(flow.getTableId()))
+                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow.class, new FlowKey(flow.getId()))
+                .toInstance();
+    }
+
+    private Future<RpcResult<TransactionStatus>> writeFlowAsync(final org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow flow, final NodeKey nodeKey) {
+        final DataModificationTransaction modification = this.dataBrokerService.beginTransaction();
+        modification.putConfigurationData(flowPath(flow, nodeKey), flow);
+        return modification.commit();
+    }
+
+    private Future<RpcResult<TransactionStatus>> internalAddFlowAsync(final Node node, final Flow flow, final long rid) {
+        final Map<Flow,UUID> cache = this.getCache();
+        UUID flowId = cache.get(flow);
+        if (flowId != null) {
+            this.removeFlow(node, flow);
+        }
+
+        flowId = UUID.randomUUID();
+        cache.put(flow, flowId);
+        return this.writeFlowAsync(MDFlowMapping.toMDFlow(flow, flowId.toString()), new NodeKey(new NodeId(node.getNodeIDString())));
+    }
+
+    private Future<RpcResult<TransactionStatus>> internalModifyFlowAsync(final Node node, final Flow oldFlow, final Flow newFlow, final long rid) {
+        final Map<Flow,UUID> cache = this.getCache();
+
+        UUID flowId = cache.remove(oldFlow);
+        if (flowId == null) {
+            flowId = UUID.randomUUID();
+            cache.put(oldFlow, flowId);
+            LOG.warn("Could not find flow {} in cache, assigned new ID {}", oldFlow.hashCode(), flowId);
+        }
+
+        cache.put(newFlow, flowId);
+        return this.writeFlowAsync(MDFlowMapping.toMDFlow(newFlow, flowId.toString()), new NodeKey(new NodeId(node.getNodeIDString())));
+    }
+
+    private Future<RpcResult<TransactionStatus>> internalRemoveFlowAsync(final Node node, final Flow adflow, final long rid) {
+        final Map<Flow,UUID> cache = this.getCache();
+
+        final UUID flowId = cache.remove(adflow);
+        if (flowId == null) {
+            LOG.warn("Could not find flow {} in cache, nothing to do", adflow.hashCode());
+            return null;
+        }
+
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow flow = MDFlowMapping.toMDFlow(adflow, flowId.toString());
+        final DataModificationTransaction modification = this.dataBrokerService.beginTransaction();
+        modification.removeConfigurationData(flowPath(flow, new NodeKey(new NodeId(node.getNodeIDString()))));
+        return modification.commit();
+    }
+
+    private static Status toFutureStatus(final Future<RpcResult<TransactionStatus>> future) {
+        if (future == null) {
+            // FIXME: really?
+            return FlowProgrammerAdapter.toStatus(true);
+        }
+
+        try {
+            final RpcResult<TransactionStatus> result = future.get();
+            return FlowProgrammerAdapter.toStatus(result);
+        } catch (final InterruptedException e) {
+            FlowProgrammerAdapter.LOG.error("Interrupted while processing flow", e);
+        } catch (ExecutionException e) {
+            FlowProgrammerAdapter.LOG.error("Failed to process flow", e);
+        }
+
+        return new Status(StatusCode.INTERNALERROR);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<Flow,UUID> getCache() {
+        final IClusterGlobalServices cgs = getClusterGlobalServices();
+        if (cgs == null) {
+            return new ConcurrentHashMap<Flow, UUID>();
+        }
+
+        Map<Flow, UUID> cache = (Map<Flow, UUID>) cgs.getCache(FlowProgrammerAdapter.CACHE_NAME);
+        if (cache != null) {
+            return cache;
+        }
+
+        try {
+            return (Map<Flow, UUID>) cgs.createCache(CACHE_NAME, EnumSet.of(cacheMode.TRANSACTIONAL));
+        } catch (CacheExistException e) {
+            return (Map<Flow, UUID>) cgs.getCache(CACHE_NAME);
+        } catch (CacheConfigException e) {
+            throw new IllegalStateException("Unexpected cache configuration problem", e);
+        }
+    }
+
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.xtend
deleted file mode 100644 (file)
index 8a0874e..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * 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.compatibility
-
-import java.util.Map
-import java.util.UUID
-import java.util.concurrent.ExecutionException
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.Future
-import java.util.EnumSet
-import org.opendaylight.controller.sal.core.Node
-import org.opendaylight.controller.sal.flowprogrammer.Flow
-import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService
-import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService
-import org.opendaylight.controller.sal.utils.Status
-import org.opendaylight.controller.sal.utils.StatusCode
-import org.opendaylight.controller.clustering.services.CacheExistException
-import org.opendaylight.controller.clustering.services.IClusterGlobalServices
-import org.opendaylight.controller.clustering.services.IClusterServices
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowListener
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeExperimenterErrorNotification
-import org.opendaylight.yangtools.yang.common.RpcResult
-import org.slf4j.LoggerFactory
-
-import org.opendaylight.controller.sal.binding.api.data.DataBrokerService
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId
-
-
-import static extension org.opendaylight.controller.sal.compatibility.MDFlowMapping.*
-
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-import static extension org.opendaylight.controller.sal.compatibility.ToSalConversionsUtils.*
-
-class FlowProgrammerAdapter implements IPluginInFlowProgrammerService, SalFlowListener {
-
-    private static val LOG = LoggerFactory.getLogger(FlowProgrammerAdapter);
-    private static val CACHE_NAME = "flowprogrammeradapter.flowtoid";
-
-    @Property
-    private SalFlowService delegate;
-
-    @Property
-    private DataBrokerService dataBrokerService;
-    
-    @Property
-    private IPluginOutFlowProgrammerService flowProgrammerPublisher;
-
-    @Property
-    private IClusterGlobalServices clusterGlobalServices;
-
-
-    @Property
-    private Map<Flow, UUID> flowToFlowId = new ConcurrentHashMap<Flow, UUID>();
-
-
-    override addFlow(Node node, Flow flow) {
-        return toFutureStatus(internalAddFlowAsync(node,flow,0));
-    }
-
-    override modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
-        return toFutureStatus(internalModifyFlowAsync(node, oldFlow,newFlow,0));
-    }
-
-    override removeFlow(Node node, Flow flow) {
-        return toFutureStatus(internalRemoveFlowAsync(node, flow,0));
-    }
-
-    override addFlowAsync(Node node, Flow flow, long rid) {
-        internalAddFlowAsync(node, flow, rid);
-        return toStatus(true);
-    }
-
-    override modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow, long rid) {
-        internalModifyFlowAsync(node, oldFlow, newFlow, rid);
-        return toStatus(true);
-    }
-
-    override removeFlowAsync(Node node, Flow flow, long rid) {
-        internalRemoveFlowAsync(node, flow, rid);
-        return toStatus(true);
-    }
-
-    override removeAllFlows(Node node) {
-        // I know this looks like a copout... but its exactly what the legacy OFplugin did
-        return new Status(StatusCode.SUCCESS);
-    }
-
-    override syncSendBarrierMessage(Node node) {
-
-        // FIXME: Update YANG model
-        return null;
-    }
-
-    override asyncSendBarrierMessage(Node node) {
-
-        // FIXME: Update YANG model
-        return null;
-    }
-
-    private static def toStatus(boolean successful) {
-        if (successful) {
-            return new Status(StatusCode.SUCCESS);
-        } else {
-            return new Status(StatusCode.INTERNALERROR);
-        }
-    }
-
-    public static def toStatus(RpcResult<?> result) {
-        return toStatus(result.isSuccessful());
-    }
-    
-    private static dispatch def Status processException(InterruptedException e) {
-        LOG.error("Interruption occured during processing flow",e);
-        return new Status(StatusCode.INTERNALERROR);
-    }
-    
-    private static dispatch def Status processException(ExecutionException e) {
-        LOG.error("Execution exception occured during processing flow",e.cause);
-        return new Status(StatusCode.INTERNALERROR);
-    }
-    
-    private static dispatch def Status processException(Exception e) {
-        throw new RuntimeException(e);
-    }
-    
-    override onFlowAdded(FlowAdded notification) {
-        // NOOP : Not supported by AD SAL
-    }
-    
-    override onFlowRemoved(FlowRemoved notification) {
-        if(notification != null && notification.node != null) {
-            val adNode = notification.node.toADNode
-            if(adNode != null) {
-                flowProgrammerPublisher.flowRemoved(adNode,notification.toFlow(adNode));
-            }
-        } 
-    }
-    
-    override onFlowUpdated(FlowUpdated notification) {
-        // NOOP : Not supported by AD SAL
-    }
-    
-    override onSwitchFlowRemoved(SwitchFlowRemoved notification) {
-        // NOOP : Not supported by AD SAL
-    }
-    
-     override onNodeErrorNotification(NodeErrorNotification notification) {
-        // NOOP : Not supported by AD SAL
-    }
-    
-     override onNodeExperimenterErrorNotification(
-                NodeExperimenterErrorNotification notification) {
-        // NOOP : Not supported by AD SAL
-    }
-
-    private def Future<RpcResult<TransactionStatus>> writeFlowAsync(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow flow, NodeKey nodeKey){
-        val modification = this._dataBrokerService.beginTransaction();
-        val flowPath = InstanceIdentifier.builder(Nodes)
-                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, nodeKey)
-                .augmentation(FlowCapableNode)
-                .child(Table, new TableKey(flow.getTableId()))
-                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow, new FlowKey(flow.id))
-                .build;
-        modification.putConfigurationData(flowPath, flow);
-        return modification.commit();
-    }
-
-    private def Future<RpcResult<TransactionStatus>> internalAddFlowAsync(Node node, Flow flow, long rid){
-        var flowId = getCache().get(flow);
-        if(flowId != null) {
-            removeFlow(node, flow);
-            return internalAddFlowAsync(node, flow, rid);
-        }
-
-        flowId = UUID.randomUUID();
-        getCache().put(flow, flowId);
-
-        return writeFlowAsync(flow.toMDFlow(flowId.toString()), new NodeKey(new NodeId(node.getNodeIDString())));
-    }
-
-    private def Future<RpcResult<TransactionStatus>> internalModifyFlowAsync(Node node, Flow oldFlow, Flow newFlow, long rid) {
-        var flowId = getCache().remove(oldFlow);
-        if(flowId == null){
-            LOG.error("oldFlow not found in cache : " + oldFlow.hashCode);
-            flowId = UUID.randomUUID();
-            getCache().put(oldFlow, flowId);
-        }
-
-        getCache().put(newFlow, flowId);
-        return writeFlowAsync(newFlow.toMDFlow(flowId.toString()), new NodeKey(new NodeId(node.getNodeIDString())));
-    }
-
-
-    private def Future<RpcResult<TransactionStatus>> internalRemoveFlowAsync(Node node, Flow adflow, long rid){
-        val flowId = getCache().remove(adflow);
-        if(flowId == null){
-            //throw new IllegalArgumentException("adflow not found in cache : " + adflow.hashCode);
-            LOG.error("adflow not found in cache : " + adflow.hashCode);
-            return null;
-        }
-        val flow = adflow.toMDFlow(flowId.toString());
-        val modification = this._dataBrokerService.beginTransaction();
-        val flowPath = InstanceIdentifier.builder(Nodes)
-                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node, new NodeKey(new NodeId(node.getNodeIDString())))
-                .augmentation(FlowCapableNode)
-                .child(Table, new TableKey(flow.getTableId()))
-                .child(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow, new FlowKey(flow.id))
-                .build;
-        modification.removeConfigurationData(flowPath);
-        return modification.commit();
-    }
-
-    private def toFutureStatus(Future<RpcResult<TransactionStatus>> future){
-        if(future == null){
-            return toStatus(true);
-        }
-
-        try {
-            val result = future.get();
-            return toStatus(result);
-        } catch (InterruptedException e) {
-            return processException(e);
-        } catch (ExecutionException e) {
-            return processException(e);
-        } catch (Exception e){
-            processException(e);
-        }
-        return toStatus(false);
-    }
-
-    private def Map<Flow, UUID> getCache(){
-        if(clusterGlobalServices == null){
-            return new ConcurrentHashMap<Flow, UUID>();
-        }
-
-        var cache = clusterGlobalServices.getCache(CACHE_NAME);
-
-        if(cache == null) {
-            try {
-                cache = clusterGlobalServices.createCache(CACHE_NAME, EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-            } catch (CacheExistException e) {
-                cache = clusterGlobalServices.getCache(CACHE_NAME);
-            }
-        }
-        return cache as Map<Flow, UUID>;
-
-    }
-
-}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.java
new file mode 100644 (file)
index 0000000..e989db4
--- /dev/null
@@ -0,0 +1,445 @@
+/**
+ * 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.compatibility;
+
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.Controller;
+import org.opendaylight.controller.sal.action.Drop;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.FloodAll;
+import org.opendaylight.controller.sal.action.HwPath;
+import org.opendaylight.controller.sal.action.Loopback;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.action.PopVlan;
+import org.opendaylight.controller.sal.action.PushVlan;
+import org.opendaylight.controller.sal.action.SetDlDst;
+import org.opendaylight.controller.sal.action.SetDlSrc;
+import org.opendaylight.controller.sal.action.SetDlType;
+import org.opendaylight.controller.sal.action.SetNextHop;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.action.SetNwSrc;
+import org.opendaylight.controller.sal.action.SetNwTos;
+import org.opendaylight.controller.sal.action.SetTpDst;
+import org.opendaylight.controller.sal.action.SetTpSrc;
+import org.opendaylight.controller.sal.action.SetVlanCfi;
+import org.opendaylight.controller.sal.action.SetVlanId;
+import org.opendaylight.controller.sal.action.SetVlanPcp;
+import org.opendaylight.controller.sal.action.SwPath;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.ControllerActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.ControllerActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.FloodActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.FloodActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.FloodAllActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.FloodAllActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.HwPathActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.HwPathActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.LoopbackActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.LoopbackActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopVlanActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopVlanActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlDstActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlDstActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlSrcActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlSrcActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlTypeActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlTypeActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNextHopActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNextHopActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwDstActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwDstActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwSrcActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwSrcActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwTosActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwTosActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanCfiActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanCfiActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanPcpActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanPcpActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SwPathActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SwPathActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.controller.action._case.ControllerActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.drop.action._case.DropActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.flood.action._case.FloodActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.flood.all.action._case.FloodAllActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.hw.path.action._case.HwPathActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.loopback.action._case.LoopbackActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.dl.dst.action._case.SetDlDstActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.dl.src.action._case.SetDlSrcActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.dl.type.action._case.SetDlTypeActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.next.hop.action._case.SetNextHopActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.nw.dst.action._case.SetNwDstActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.nw.src.action._case.SetNwSrcActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.nw.tos.action._case.SetNwTosActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.dst.action._case.SetTpDstActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.src.action._case.SetTpSrcActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.vlan.cfi.action._case.SetVlanCfiActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.vlan.id.action._case.SetVlanIdActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.vlan.pcp.action._case.SetVlanPcpActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.sw.path.action._case.SwPathActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv6Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAddedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanPcp;
+
+import com.google.common.base.Preconditions;
+import com.google.common.net.InetAddresses;
+
+public final class MDFlowMapping {
+    private MDFlowMapping() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    private static List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> toMDActions(final List<Action> actions) {
+        final ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> ret =
+                new ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action>(actions.size());
+        int action = 0;
+        for (final Action sourceAction : actions) {
+            ret.add(toAction(sourceAction, action));
+            action++;
+        }
+
+        return ret;
+    }
+
+    public static FlowAdded flowAdded(final Flow sourceFlow) {
+        Preconditions.checkArgument(sourceFlow != null);
+
+        return new FlowAddedBuilder()
+        .setHardTimeout(Integer.valueOf(sourceFlow.getHardTimeout()))
+        .setIdleTimeout(Integer.valueOf(sourceFlow.getIdleTimeout()))
+        .setCookie(new FlowCookie(BigInteger.valueOf(sourceFlow.getId())))
+        .setPriority(Integer.valueOf(sourceFlow.getPriority()))
+        .setInstructions(MDFlowMapping.toApplyInstruction(toMDActions(sourceFlow.getActions())))
+        .setMatch(FromSalConversionsUtils.toMatch(sourceFlow.getMatch()))
+        .setTableId((short)0)
+        .build();
+    }
+
+    private static FlowBuilder internalToMDFlow(final Flow sourceFlow) {
+        Preconditions.checkArgument(sourceFlow != null);
+
+        return new FlowBuilder()
+        .setHardTimeout(Integer.valueOf(sourceFlow.getHardTimeout()))
+        .setIdleTimeout(Integer.valueOf(sourceFlow.getIdleTimeout()))
+        .setCookie(new FlowCookie(BigInteger.valueOf(sourceFlow.getId())))
+        .setPriority(Integer.valueOf((sourceFlow.getPriority())))
+        .setInstructions(MDFlowMapping.toApplyInstruction(toMDActions(sourceFlow.getActions())))
+        .setMatch(FromSalConversionsUtils.toMatch(sourceFlow.getMatch()));
+    }
+
+    public static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow toMDFlow(final Flow sourceFlow, final String flowId) {
+        return internalToMDFlow(sourceFlow)
+                .setTableId((short)0)
+                .setId(new FlowId(flowId))
+                .build();
+    }
+
+    public static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow toMDSalflow(final Flow sourceFlow) {
+        return internalToMDFlow(sourceFlow).build();
+    }
+
+    public static Instructions toApplyInstruction(final List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions) {
+        return new InstructionsBuilder()
+        .setInstruction(
+                Collections.singletonList(
+                        new InstructionBuilder()
+                        .setOrder(0)
+                        .setInstruction(
+                                new ApplyActionsCaseBuilder()
+                                .setApplyActions(new ApplyActionsBuilder().setAction(actions).build())
+                                .build()
+                                ).build())
+                ).build();
+    }
+
+    public static RemoveFlowInput removeFlowInput(final Node sourceNode, final Flow sourceFlow) {
+        final FlowAdded source = MDFlowMapping.flowAdded(sourceFlow);
+        return new RemoveFlowInputBuilder((org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow) source)
+        .setNode(NodeMapping.toNodeRef(sourceNode))
+        .build();
+    }
+
+    public static AddFlowInput addFlowInput(final Node sourceNode, final Flow sourceFlow) {
+        final FlowAdded source = MDFlowMapping.flowAdded(sourceFlow);
+        return new AddFlowInputBuilder(((org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow) source))
+        .setNode(NodeMapping.toNodeRef(sourceNode))
+        .build();
+    }
+
+    public static UpdateFlowInput updateFlowInput(final Node sourceNode, final Flow oldFlow, final Flow newFlow) {
+        return new UpdateFlowInputBuilder()
+        .setOriginalFlow(new OriginalFlowBuilder(MDFlowMapping.flowAdded(newFlow)).build())
+        .setUpdatedFlow(new UpdatedFlowBuilder(MDFlowMapping.flowAdded(newFlow)).build())
+        .setNode(NodeMapping.toNodeRef(sourceNode))
+        .build();
+    }
+
+    private static ControllerActionCase _toAction(final Controller sourceAction) {
+        return new ControllerActionCaseBuilder().setControllerAction(new ControllerActionBuilder().build()).build();
+    }
+
+    private static DropActionCase _toAction(final Drop sourceAction) {
+        return new DropActionCaseBuilder().setDropAction(new DropActionBuilder().build()).build();
+    }
+
+    private static FloodActionCase _toAction(final Flood sourceAction) {
+        return new FloodActionCaseBuilder().setFloodAction(new FloodActionBuilder().build()).build();
+    }
+
+    private static FloodAllActionCase _toAction(final FloodAll sourceAction) {
+        return new FloodAllActionCaseBuilder().setFloodAllAction(new FloodAllActionBuilder().build()).build();
+    }
+
+    private static HwPathActionCase _toAction(final HwPath sourceAction) {
+        return new HwPathActionCaseBuilder().setHwPathAction(new HwPathActionBuilder().build()).build();
+    }
+
+    private static LoopbackActionCase _toAction(final Loopback sourceAction) {
+        return new LoopbackActionCaseBuilder().setLoopbackAction( new LoopbackActionBuilder().build()).build();
+    }
+
+    private static OutputActionCase _toAction(final Output sourceAction) {
+        return new OutputActionCaseBuilder()
+        .setOutputAction(
+                new OutputActionBuilder().setOutputNodeConnector(MDFlowMapping.toUri(sourceAction.getPort())).build()
+                ).build();
+    }
+
+    private static PopVlanActionCase _toAction(final PopVlan sourceAction) {
+        return new PopVlanActionCaseBuilder().build();
+    }
+
+    private static PushVlanActionCase _toAction(final PushVlan sourceAction) {
+        return new PushVlanActionCaseBuilder()
+        .setPushVlanAction(
+                new PushVlanActionBuilder()
+                .setCfi(new VlanCfi(sourceAction.getCfi()))
+                .setPcp(sourceAction.getPcp())
+                .setTag(sourceAction.getTag())
+                .setVlanId(new VlanId(sourceAction.getVlanId()))
+                .build()
+                ).build();
+    }
+
+    private static SetDlDstActionCase _toAction(final SetDlDst sourceAction) {
+        return new SetDlDstActionCaseBuilder()
+        .setSetDlDstAction(new SetDlDstActionBuilder().setAddress(MDFlowMapping.toMacAddress(sourceAction.getDlAddress())).build())
+        .build();
+    }
+
+    private static SetDlSrcActionCase _toAction(final SetDlSrc sourceAction) {
+        return new SetDlSrcActionCaseBuilder()
+        .setSetDlSrcAction(new SetDlSrcActionBuilder().setAddress(MDFlowMapping.toMacAddress(sourceAction.getDlAddress())).build())
+        .build();
+    }
+
+    private static SetDlTypeActionCase _toAction(final SetDlType sourceAction) {
+        return new SetDlTypeActionCaseBuilder()
+        .setSetDlTypeAction(new SetDlTypeActionBuilder().setDlType(new EtherType(Long.valueOf(sourceAction.getDlType()))).build())
+        .build();
+    }
+
+    private static SetNextHopActionCase _toAction(final SetNextHop sourceAction) {
+        return new SetNextHopActionCaseBuilder()
+        .setSetNextHopAction(new SetNextHopActionBuilder().setAddress(MDFlowMapping.toInetAddress(sourceAction.getAddress())).build())
+        .build();
+    }
+
+    private static SetNwDstActionCase _toAction(final SetNwDst sourceAction) {
+        return new SetNwDstActionCaseBuilder()
+        .setSetNwDstAction(new SetNwDstActionBuilder().setAddress(MDFlowMapping.toInetAddress(sourceAction.getAddress())).build())
+        .build();
+    }
+
+    private static SetNwSrcActionCase _toAction(final SetNwSrc sourceAction) {
+        return new SetNwSrcActionCaseBuilder()
+        .setSetNwSrcAction(new SetNwSrcActionBuilder().setAddress(MDFlowMapping.toInetAddress(sourceAction.getAddress())).build())
+        .build();
+    }
+
+    private static SetNwTosActionCase _toAction(final SetNwTos sourceAction) {
+        return new SetNwTosActionCaseBuilder()
+        .setSetNwTosAction(new SetNwTosActionBuilder().setTos(sourceAction.getNwTos()).build())
+        .build();
+    }
+
+    private static SetTpDstActionCase _toAction(final SetTpDst sourceAction) {
+        return new SetTpDstActionCaseBuilder()
+        .setSetTpDstAction(new SetTpDstActionBuilder().setPort(new PortNumber(sourceAction.getPort())).build())
+        .build();
+    }
+
+    private static SetTpSrcActionCase _toAction(final SetTpSrc sourceAction) {
+        return new SetTpSrcActionCaseBuilder()
+        .setSetTpSrcAction(new SetTpSrcActionBuilder().setPort(new PortNumber(sourceAction.getPort())).build())
+        .build();
+    }
+
+    private static SetVlanCfiActionCase _toAction(final SetVlanCfi sourceAction) {
+        return new SetVlanCfiActionCaseBuilder()
+        .setSetVlanCfiAction(new SetVlanCfiActionBuilder().setVlanCfi(new VlanCfi(sourceAction.getCfi())).build())
+        .build();
+    }
+
+    private static SetVlanIdActionCase _toAction(final SetVlanId sourceAction) {
+        return new SetVlanIdActionCaseBuilder()
+        .setSetVlanIdAction(new SetVlanIdActionBuilder().setVlanId(new VlanId(sourceAction.getVlanId())).build())
+        .build();
+    }
+
+    private static SetVlanPcpActionCase _toAction(final SetVlanPcp sourceAction) {
+        return new SetVlanPcpActionCaseBuilder()
+        .setSetVlanPcpAction(new SetVlanPcpActionBuilder().setVlanPcp(new VlanPcp((short) sourceAction.getPcp())).build())
+        .build();
+    }
+
+    private static SwPathActionCase _toAction(final SwPath sourceAction) {
+        return new SwPathActionCaseBuilder().setSwPathAction(new SwPathActionBuilder().build()).build();
+    }
+
+    public static Uri toUri(final NodeConnector connector) {
+        return new NodeConnectorId(((String) connector.getID()));
+    }
+
+    public static MacAddress toMacAddress(final byte[] bytes) {
+        final StringBuilder sb = new StringBuilder(18);
+        boolean first = true;
+
+        for (final byte b : bytes) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(':');
+            }
+            sb.append(String.format("%02x", Byte.valueOf(b)));
+        }
+        return new MacAddress(sb.toString());
+    }
+
+    public static org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action toAction(final Action sourceAction, final int order) {
+        final ActionBuilder ret = new ActionBuilder().setOrder(order);
+
+        if (sourceAction instanceof Controller) {
+            ret.setAction(_toAction((Controller)sourceAction));
+        } else if (sourceAction instanceof Drop) {
+            ret.setAction(_toAction((Drop)sourceAction));
+        } else if (sourceAction instanceof Flood) {
+            ret.setAction(_toAction((Flood)sourceAction));
+        } else if (sourceAction instanceof FloodAll) {
+            ret.setAction(_toAction((FloodAll)sourceAction));
+        } else if (sourceAction instanceof HwPath) {
+            ret.setAction(_toAction((HwPath)sourceAction));
+        } else if (sourceAction instanceof Loopback) {
+            ret.setAction(_toAction((Loopback)sourceAction));
+        } else if (sourceAction instanceof Output) {
+            ret.setAction(_toAction((Output)sourceAction));
+        } else if (sourceAction instanceof PopVlan) {
+            ret.setAction(_toAction((PopVlan)sourceAction));
+        } else if (sourceAction instanceof PushVlan) {
+            ret.setAction(_toAction((PushVlan)sourceAction));
+        } else if (sourceAction instanceof SetDlDst) {
+            ret.setAction(_toAction((SetDlDst)sourceAction));
+        } else if (sourceAction instanceof SetDlSrc) {
+            ret.setAction(_toAction((SetDlSrc)sourceAction));
+        } else if (sourceAction instanceof SetDlType) {
+            ret.setAction(_toAction((SetDlType)sourceAction));
+        } else if (sourceAction instanceof SetNextHop) {
+            ret.setAction(_toAction((SetNextHop)sourceAction));
+        } else if (sourceAction instanceof SetNwDst) {
+            ret.setAction(_toAction((SetNwDst)sourceAction));
+        } else if (sourceAction instanceof SetNwSrc) {
+            ret.setAction(_toAction((SetNwSrc)sourceAction));
+        } else if (sourceAction instanceof SetNwTos) {
+            ret.setAction(_toAction((SetNwTos)sourceAction));
+        } else if (sourceAction instanceof SetTpDst) {
+            ret.setAction(_toAction((SetTpDst)sourceAction));
+        } else if (sourceAction instanceof SetTpSrc) {
+            ret.setAction(_toAction((SetTpSrc)sourceAction));
+        } else if (sourceAction instanceof SetVlanCfi) {
+            ret.setAction(_toAction((SetVlanCfi)sourceAction));
+        } else if (sourceAction instanceof SetVlanId) {
+            ret.setAction(_toAction((SetVlanId)sourceAction));
+        } else if (sourceAction instanceof SetVlanPcp) {
+            ret.setAction(_toAction((SetVlanPcp)sourceAction));
+        } else if (sourceAction instanceof SwPath) {
+            ret.setAction(_toAction((SwPath)sourceAction));
+        } else {
+            throw new IllegalArgumentException(String.format("Unhandled action class %s", sourceAction.getClass()));
+        }
+
+        return ret.build();
+    }
+
+    public static Address toInetAddress(final InetAddress address) {
+        if (address instanceof Inet4Address) {
+            return new Ipv4Builder()
+            .setIpv4Address(new Ipv4Prefix(InetAddresses.toAddrString(address)))
+            .build();
+        }
+        if (address instanceof Inet6Address) {
+            return new Ipv6Builder()
+            .setIpv6Address(new Ipv6Prefix(InetAddresses.toAddrString(address)))
+            .build();
+        }
+
+        throw new IllegalArgumentException(String.format("Unhandled address class %s", address.getClass()));
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/MDFlowMapping.xtend
deleted file mode 100644 (file)
index 75cbf49..0000000
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * 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.compatibility;
-
-import com.google.common.net.InetAddresses
-import java.math.BigInteger
-import java.net.Inet4Address
-import java.net.Inet6Address
-import java.util.ArrayList
-import org.opendaylight.controller.sal.action.Controller
-import org.opendaylight.controller.sal.action.Drop
-import org.opendaylight.controller.sal.action.Flood
-import org.opendaylight.controller.sal.action.FloodAll
-import org.opendaylight.controller.sal.action.HwPath
-import org.opendaylight.controller.sal.action.Loopback
-import org.opendaylight.controller.sal.action.Output
-import org.opendaylight.controller.sal.action.PopVlan
-import org.opendaylight.controller.sal.action.PushVlan
-import org.opendaylight.controller.sal.action.SetDlDst
-import org.opendaylight.controller.sal.action.SetDlSrc
-import org.opendaylight.controller.sal.action.SetDlType
-import org.opendaylight.controller.sal.action.SetNextHop
-import org.opendaylight.controller.sal.action.SetNwDst
-import org.opendaylight.controller.sal.action.SetNwSrc
-import org.opendaylight.controller.sal.action.SetNwTos
-import org.opendaylight.controller.sal.action.SetTpDst
-import org.opendaylight.controller.sal.action.SetTpSrc
-import org.opendaylight.controller.sal.action.SetVlanCfi
-import org.opendaylight.controller.sal.action.SetVlanId
-import org.opendaylight.controller.sal.action.SetVlanPcp
-import org.opendaylight.controller.sal.action.SwPath
-import org.opendaylight.controller.sal.core.Node
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.flowprogrammer.Flow
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAddedBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.Address
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4Builder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv6Builder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId
-import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanPcp
-
-import static extension org.opendaylight.controller.sal.compatibility.FromSalConversionsUtils.*
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder
-import java.util.Collections
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.controller.action._case.ControllerActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.drop.action._case.DropActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.flood.action._case.FloodActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.flood.all.action._case.FloodAllActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.hw.path.action._case.HwPathActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.loopback.action._case.LoopbackActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.dl.dst.action._case.SetDlDstActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.dl.src.action._case.SetDlSrcActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.dl.type.action._case.SetDlTypeActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.next.hop.action._case.SetNextHopActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.nw.dst.action._case.SetNwDstActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.nw.src.action._case.SetNwSrcActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.nw.tos.action._case.SetNwTosActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.dst.action._case.SetTpDstActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.src.action._case.SetTpSrcActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.vlan.cfi.action._case.SetVlanCfiActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.vlan.id.action._case.SetVlanIdActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.vlan.pcp.action._case.SetVlanPcpActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.sw.path.action._case.SwPathActionBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwTosActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwSrcActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNwDstActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetNextHopActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlTypeActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlDstActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.ControllerActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.FloodActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.FloodAllActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.HwPathActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.LoopbackActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PopVlanActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetDlSrcActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanCfiActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanPcpActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SwPathActionCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie
-
-public class MDFlowMapping {
-
-    private new() {
-        throw new UnsupportedOperationException()
-    }
-
-    public static def flowAdded(Flow sourceFlow) {
-        if (sourceFlow == null)
-            throw new IllegalArgumentException();
-        val it = new FlowAddedBuilder();
-
-        hardTimeout = sourceFlow.hardTimeout as int
-        idleTimeout = sourceFlow.idleTimeout as int
-        cookie = new FlowCookie(BigInteger.valueOf(sourceFlow.id))
-        priority = sourceFlow.priority as int
-
-        val sourceActions = sourceFlow.actions;
-        val targetActions = new ArrayList<Action>();
-        var int action = 0;
-        for (sourceAction : sourceActions) {
-            targetActions.add(sourceAction.toAction(action));
-            action = action + 1;
-        }
-        instructions = targetActions.toApplyInstruction();
-        match = sourceFlow.match.toMatch();
-        tableId = new Integer(0).shortValue
-        return it.build();
-
-    }
-    
-    public static def toMDFlow(Flow sourceFlow, String flowId) {
-       if (sourceFlow == null)
-            throw new IllegalArgumentException();
-       val it = new FlowBuilder();
-       hardTimeout = sourceFlow.hardTimeout as int
-       idleTimeout = sourceFlow.idleTimeout as int
-       cookie = new FlowCookie(BigInteger.valueOf(sourceFlow.id))
-       priority = sourceFlow.priority as int
-       id = new FlowId(flowId)
-    
-       val sourceActions = sourceFlow.actions;
-       val targetActions = new ArrayList<Action>();
-       var int action = 0;
-       for (sourceAction : sourceActions) {
-           targetActions.add(sourceAction.toAction(action));
-           action = action+ 1;
-       }
-       instructions = targetActions.toApplyInstruction();
-       match = sourceFlow.match.toMatch();
-       tableId = new Integer(0).shortValue
-       return it.build();
-    }
-    
-    public static def Instructions toApplyInstruction(ArrayList<Action> actions) {
-        val it = new InstructionsBuilder;
-        val applyActions = new InstructionBuilder;
-        applyActions.instruction = new ApplyActionsCaseBuilder().setApplyActions(new ApplyActionsBuilder().setAction(actions).build()).build()
-        applyActions.setOrder(new Integer(0))
-        instruction = Collections.<Instruction>singletonList(applyActions.build)
-        return it.build;
-    }
-
-    public static def removeFlowInput(Node sourceNode, Flow sourceFlow) {
-        val source = flowAdded(sourceFlow);
-        val it = new RemoveFlowInputBuilder(source as org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow);
-        node = sourceNode.toNodeRef()
-        return it.build();
-    }
-
-    public static def addFlowInput(Node sourceNode, Flow sourceFlow) {
-        val source = flowAdded(sourceFlow);
-        val it = new AddFlowInputBuilder(source as org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow);
-        it.setNode(sourceNode.toNodeRef)
-        return it.build();
-    }
-
-    public static def updateFlowInput(Node sourceNode, Flow oldFlow, Flow newFlow) {
-        val it = new UpdateFlowInputBuilder();
-        val sourceOld = flowAdded(newFlow);
-
-        val original = new OriginalFlowBuilder(sourceOld as org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow);
-        val sourceNew = flowAdded(newFlow);
-        val updated = new UpdatedFlowBuilder(sourceNew as org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow);
-        originalFlow = original.build()
-        updatedFlow = updated.build();
-        node = sourceNode.toNodeRef()
-        return it.build();
-    }
-
-    public static dispatch def toAction(Controller sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new ControllerActionCaseBuilder().setControllerAction(new ControllerActionBuilder().build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(Drop sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new DropActionCaseBuilder().setDropAction(new DropActionBuilder().build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(Flood sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new FloodActionCaseBuilder().setFloodAction(new FloodActionBuilder().build).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(FloodAll sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new FloodAllActionCaseBuilder().setFloodAllAction(new FloodAllActionBuilder().build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(HwPath sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new HwPathActionCaseBuilder().setHwPathAction(new HwPathActionBuilder().build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(Loopback sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new LoopbackActionCaseBuilder().setLoopbackAction(new LoopbackActionBuilder().build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(Output sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new OutputActionBuilder();
-        outputNodeConnector = sourceAction.port.toUri;
-        actionBuilder.action = new OutputActionCaseBuilder().setOutputAction(it.build()).build();
-        return actionBuilder.build();
-
-    }
-
-    public static dispatch def toAction(PopVlan sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new PopVlanActionCaseBuilder().build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(PushVlan sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new PushVlanActionBuilder();
-        cfi = new VlanCfi(sourceAction.cfi);
-        vlanId = new VlanId(sourceAction.vlanId);
-        pcp = sourceAction.pcp;
-        tag = sourceAction.tag;
-        actionBuilder.action = new PushVlanActionCaseBuilder().setPushVlanAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetDlDst sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetDlDstActionBuilder();
-        address = sourceAction.dlAddress.toMacAddress();
-        actionBuilder.action = new SetDlDstActionCaseBuilder().setSetDlDstAction(it.build()).build;
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetDlSrc sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetDlSrcActionBuilder();
-        address = sourceAction.dlAddress.toMacAddress;
-        actionBuilder.action = new SetDlSrcActionCaseBuilder().setSetDlSrcAction(it.build()).build;
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetDlType sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetDlTypeActionBuilder();
-        dlType = new EtherType(sourceAction.dlType as long);
-        actionBuilder.action = new SetDlTypeActionCaseBuilder().setSetDlTypeAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetNextHop sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetNextHopActionBuilder();
-        val inetAddress = sourceAction.address;
-        address = inetAddress.toInetAddress;
-        actionBuilder.action = new SetNextHopActionCaseBuilder().setSetNextHopAction(it.build).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetNwDst sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetNwDstActionBuilder();
-        val inetAddress = sourceAction.address;
-        address = inetAddress.toInetAddress;
-        actionBuilder.action = new SetNwDstActionCaseBuilder().setSetNwDstAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetNwSrc sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetNwSrcActionBuilder();
-        val inetAddress = sourceAction.address;
-        address = inetAddress.toInetAddress;
-        actionBuilder.action = new SetNwSrcActionCaseBuilder().setSetNwSrcAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetNwTos sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetNwTosActionBuilder();
-        tos = sourceAction.nwTos;
-        actionBuilder.action = new SetNwTosActionCaseBuilder().setSetNwTosAction(it.build).build;
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetTpDst sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetTpDstActionBuilder();
-        port = new PortNumber(sourceAction.port);
-        actionBuilder.action = new SetTpDstActionCaseBuilder().setSetTpDstAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetTpSrc sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetTpSrcActionBuilder();
-        port = new PortNumber(sourceAction.port);
-        actionBuilder.action = new SetTpSrcActionCaseBuilder().setSetTpSrcAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetVlanCfi sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetVlanCfiActionBuilder();
-        vlanCfi = new VlanCfi(sourceAction.cfi);
-        actionBuilder.action = new SetVlanCfiActionCaseBuilder().setSetVlanCfiAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetVlanId sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-
-        val it = new SetVlanIdActionBuilder();
-        vlanId = new VlanId(sourceAction.vlanId);
-        actionBuilder.action = new SetVlanIdActionCaseBuilder().setSetVlanIdAction(it.build()).build();
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SetVlanPcp sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        val it = new SetVlanPcpActionBuilder();
-        vlanPcp = new VlanPcp(sourceAction.pcp as short);
-        actionBuilder.action = new SetVlanPcpActionCaseBuilder().setSetVlanPcpAction(it.build).build;
-        return actionBuilder.build();
-    }
-
-    public static dispatch def toAction(SwPath sourceAction, int order) {
-        val actionBuilder = new ActionBuilder().setOrder(order);
-        actionBuilder.action = new SwPathActionCaseBuilder().setSwPathAction(new SwPathActionBuilder().build()).build();
-        return actionBuilder.build();
-    }
-
-    public static def dispatch Address toInetAddress(Inet4Address address) {
-        val it = new Ipv4Builder
-        ipv4Address = new Ipv4Prefix(InetAddresses.toAddrString(address))
-        return it.build()
-    }
-
-    public static def dispatch Address toInetAddress(Inet6Address address) {
-        val it = new Ipv6Builder
-        ipv6Address = new Ipv6Prefix(InetAddresses.toAddrString(address))
-        return it.build()
-    }
-
-    public static def Uri toUri(NodeConnector connector) {
-        return new NodeConnectorId(connector.ID as String);
-    }
-
-    public static def MacAddress toMacAddress(byte[] bytes) {
-        val sb = new StringBuilder(18);
-        for (byte b : bytes) {
-            if (sb.length() > 0)
-                sb.append(':');
-            sb.append(String.format("%02x", b));
-        }
-        return new MacAddress(sb.toString());
-    }
-       
-       public static def toMDSalflow(Flow sourceFlow) {
-        if (sourceFlow == null)
-            throw new IllegalArgumentException();
-        val it = new FlowBuilder();
-
-        hardTimeout = sourceFlow.hardTimeout as int
-        idleTimeout = sourceFlow.idleTimeout as int
-        cookie = new FlowCookie(BigInteger.valueOf(sourceFlow.id))
-        priority = sourceFlow.priority as int
-
-        val sourceActions = sourceFlow.actions;
-        val targetActions = new ArrayList<Action>();
-        var int action = 0;
-        for (sourceAction : sourceActions) {
-            targetActions.add(sourceAction.toAction(action));
-            action = action + 1;
-        }
-        instructions = targetActions.toApplyInstruction();
-        match = sourceFlow.match.toMatch();
-        return it.build();
-       }
-       
-}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/SalCompatibilityProvider.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/SalCompatibilityProvider.java
new file mode 100644 (file)
index 0000000..0ddbcaa
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * 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.compatibility;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.compatibility.adsal.DataPacketServiceAdapter;
+import org.opendaylight.controller.sal.compatibility.topology.TopologyAdapter;
+import org.opendaylight.controller.sal.compatibility.topology.TopologyProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.OpendaylightFlowTableStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+import com.google.common.base.Preconditions;
+
+class SalCompatibilityProvider implements BindingAwareProvider {
+    private final ComponentActivator activator;
+
+    public SalCompatibilityProvider(final ComponentActivator cmpAct) {
+        this.activator = Preconditions.checkNotNull(cmpAct);
+    }
+
+    @Override
+    public Collection<? extends ProviderFunctionality> getFunctionality() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Collection<? extends RpcService> getImplementations() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public void onSessionInitialized(final ConsumerContext session) {
+        // No-op
+    }
+
+    @Override
+    public void onSessionInitiated(final ProviderContext session) {
+        final NotificationService subscribe = session.getSALService(NotificationService.class);
+
+        final FlowProgrammerAdapter flow = activator.getFlow();
+        flow.setDelegate(session.getRpcService(SalFlowService.class));
+        flow.setDataBrokerService(session.getSALService(DataBrokerService.class));
+        // FIXME: remember registration for clean shutdown
+        subscribe.registerNotificationListener(flow);
+
+        final InventoryAndReadAdapter inv = activator.getInventory();
+        inv.setDataService(session.getSALService(DataBrokerService.class));
+        inv.setFlowStatisticsService(session.getRpcService(OpendaylightFlowStatisticsService.class));
+        inv.setFlowTableStatisticsService(session.getRpcService(OpendaylightFlowTableStatisticsService.class));
+        inv.setNodeConnectorStatisticsService(session.getRpcService(OpendaylightPortStatisticsService.class));
+        inv.setTopologyDiscovery(session.getRpcService(FlowTopologyDiscoveryService.class));
+        inv.setDataProviderService(session.getSALService(DataProviderService.class));
+        // FIXME: remember registration for clean shutdown
+        subscribe.registerNotificationListener(inv);
+
+        final DataPacketServiceAdapter dps = activator.getDataPacketService();
+        dps.setDelegate(session.getRpcService(PacketProcessingService.class));
+
+        final TopologyAdapter topo = activator.getTopology();
+        topo.setDataService(session.getSALService(DataProviderService.class));
+
+        final TopologyProvider tpp = activator.getTpProvider();
+        tpp.setDataService(session.getSALService(DataProviderService.class));
+
+        inv.startAdapter();
+        tpp.startAdapter();
+
+        subscribe.registerNotificationListener(activator.getDataPacket());
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyAdapter.java
new file mode 100644 (file)
index 0000000..4e3cf69
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.compatibility.topology;
+
+import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.topology.IPluginInTopologyService;
+import org.opendaylight.controller.sal.topology.IPluginOutTopologyService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Preconditions;
+
+public class TopologyAdapter implements IPluginInTopologyService {
+    private final InstanceIdentifier<Topology> topology = InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(new TopologyId("flow:1"))).toInstance();
+
+    // Injected via Apache DM
+    private IPluginOutTopologyService topologyPublisher;
+
+
+    private DataProviderService dataService;
+
+    public void setDataService(final DataProviderService dataService) {
+        this.dataService = Preconditions.checkNotNull(dataService);
+    }
+
+    @Override
+    public void sollicitRefresh() {
+        final TypeSafeDataReader reader = TypeSafeDataReader.forReader(dataService);
+        final Topology t = reader.readOperationalData(topology);
+        topologyPublisher.edgeUpdate(TopologyMapping.toADEdgeUpdates(t, reader));
+    }
+
+    public IPluginOutTopologyService getTopologyPublisher() {
+        return topologyPublisher;
+    }
+
+    public void setTopologyPublisher(final IPluginOutTopologyService topologyPublisher) {
+        this.topologyPublisher = topologyPublisher;
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyAdapter.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyAdapter.xtend
deleted file mode 100644 (file)
index 4ce57af..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.compatibility.topology
-
-import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService
-import org.opendaylight.controller.sal.topology.IPluginInTopologyService
-import org.opendaylight.controller.sal.topology.IPluginOutTopologyService
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-
-import static extension org.opendaylight.controller.sal.compatibility.topology.TopologyMapping.*
-
-class TopologyAdapter implements IPluginInTopologyService {
-    
-    @Property
-    DataProviderService dataService;
-    
-    @Property
-    IPluginOutTopologyService topologyPublisher;
-    
-    override sollicitRefresh() {
-        val path = InstanceIdentifier.builder(NetworkTopology).child(Topology,new TopologyKey(new TopologyId("flow:1"))).toInstance;
-        val reader = TypeSafeDataReader.forReader(dataService)
-        val topology = reader.readOperationalData(path)
-        topologyPublisher.edgeUpdate(topology.toADEdgeUpdates(reader))
-    }
-    
-}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyMapping.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyMapping.java
new file mode 100644 (file)
index 0000000..476a71a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.compatibility.topology;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader;
+import org.opendaylight.controller.sal.compatibility.NodeMapping;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Edge;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.Property;
+import org.opendaylight.controller.sal.core.UpdateType;
+import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+
+public final class TopologyMapping {
+    private static final Logger LOG = LoggerFactory.getLogger(TopologyMapping.class);
+
+    private TopologyMapping() {
+        throw new UnsupportedOperationException("Utility class. Instantiation is not allowed.");
+    }
+
+    public static List<TopoEdgeUpdate> toADEdgeUpdates(final Topology topology,final TypeSafeDataReader reader) {
+        final List<TopoEdgeUpdate> result = new CopyOnWriteArrayList<>();
+        return FluentIterable.from(topology.getLink()).transform(
+                new Function<Link, TopoEdgeUpdate>() {
+                    @Override
+                    public TopoEdgeUpdate apply(final Link input) {
+                        try {
+                            return toTopoEdgeUpdate(toAdEdge(input, topology), reader);
+                        } catch (ConstructionException e) {
+                            throw new IllegalArgumentException(String.format("Failed to construct edge update for {}", input), e);
+                        }
+                    }}
+                ).copyInto(result);
+    }
+
+    public static Edge toAdEdge(final Link link, final Topology topology) throws ConstructionException {
+        final NodeConnector adSrc = toADNodeConnector(link.getSource().getSourceTp(), link.getSource().getSourceNode());
+        final NodeConnector adDst = toADNodeConnector(link.getDestination().getDestTp(), link.getDestination().getDestNode());
+        return new Edge(adSrc, adDst);
+    }
+
+    public static TopoEdgeUpdate toTopoEdgeUpdate(final Edge e, final TypeSafeDataReader reader) {
+        return toTopoEdgeUpdate(e, UpdateType.ADDED, reader);
+    }
+
+    public static TopoEdgeUpdate toTopoEdgeUpdate(final Edge e,final UpdateType type,final TypeSafeDataReader reader) {
+        return new TopoEdgeUpdate(e, toAdEdgeProperties(e, reader), type);
+    }
+
+    public static Set<Property> toAdEdgeProperties(final Edge e,final TypeSafeDataReader reader) {
+        final NodeConnectorRef ncref = NodeMapping.toNodeConnectorRef(e.getTailNodeConnector());
+        if(ncref == null) {
+            LOG.debug("Edge {} ncref {}",e,ncref);
+            return null;
+        }
+
+        @SuppressWarnings("unchecked")
+        final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector> ncInstanceId =
+        (InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector>) ncref.getValue();
+        if(ncInstanceId == null) {
+            LOG.debug("Edge {} ncref {}",e,ncref);
+            return null;
+        }
+
+        final  org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector nc = reader.readOperationalData(ncInstanceId);
+        if(nc == null) {
+            return null;
+        }
+        return NodeMapping.toADNodeConnectorProperties(nc);
+    }
+
+    public static String toADNodeId(final NodeId nodeId) {
+        return nodeId.getValue();
+    }
+
+    public static NodeConnector toADNodeConnector(final TpId source, final NodeId nodeId) throws ConstructionException {
+        checkNotNull(source);
+        return new NodeConnector(NodeMapping.MD_SAL_TYPE, toADNodeConnectorId(source), toADNode(nodeId));
+    }
+
+    public static String toADNodeConnectorId(final TpId nodeConnectorId) {
+        return nodeConnectorId.getValue();
+    }
+
+    public static Node toADNode(final NodeId nodeId) throws ConstructionException {
+        checkNotNull(nodeId);
+        return new Node(NodeMapping.MD_SAL_TYPE, toADNodeId(nodeId));
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyMapping.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyMapping.xtend
deleted file mode 100644 (file)
index 2d49056..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.compatibility.topology
-
-import com.google.common.collect.FluentIterable
-import java.util.List
-import java.util.concurrent.CopyOnWriteArrayList
-import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader
-import org.opendaylight.controller.sal.core.ConstructionException
-import org.opendaylight.controller.sal.core.Edge
-import org.opendaylight.controller.sal.core.Node
-import org.opendaylight.controller.sal.core.NodeConnector
-import org.opendaylight.controller.sal.core.UpdateType
-import org.opendaylight.controller.sal.topology.TopoEdgeUpdate
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.slf4j.LoggerFactory
-
-import static com.google.common.base.Preconditions.*
-
-import static extension org.opendaylight.controller.sal.compatibility.NodeMapping.*
-
-class TopologyMapping {
-    private static val LOG = LoggerFactory.getLogger(TopologyMapping);
-    private new() {
-        throw new UnsupportedOperationException("Utility class. Instantiation is not allowed.");
-    }
-    
-    public static def toADEdgeUpdates(Topology topology,TypeSafeDataReader reader) {
-        val List<TopoEdgeUpdate> result = new CopyOnWriteArrayList<TopoEdgeUpdate>()
-        return FluentIterable.from(topology.link).transform[toAdEdge(topology).toTopoEdgeUpdate(reader)].copyInto(result)
-    }
-    
-    public static def toAdEdge(Link link,Topology topology) {
-        val adSrc = link.source.sourceTp.toADNodeConnector(link.source.sourceNode)
-        val adDst = link.destination.destTp.toADNodeConnector(link.destination.destNode)
-        return new Edge(adSrc,adDst); 
-    }
-    
-    public static def toTopoEdgeUpdate(Edge e,TypeSafeDataReader reader) {
-        return toTopoEdgeUpdate(e,UpdateType.ADDED,reader)
-    }
-    
-    public static def toTopoEdgeUpdate(Edge e,UpdateType type,TypeSafeDataReader reader) {
-        return new TopoEdgeUpdate(e,e.toAdEdgeProperties(reader),type)
-    }
-    
-    public static def toAdEdgeProperties(Edge e,TypeSafeDataReader reader) {
-        val ncref = e.tailNodeConnector.toNodeConnectorRef
-        if(ncref == null) {
-            LOG.debug("Edge {} ncref {}",e,ncref)
-            return null;
-        }
-        val ncInstanceId = (ncref.value as InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector>)
-        if(ncInstanceId == null) {
-            LOG.debug("Edge {} ncref {}",e,ncref)
-            return null;
-        }
-        val nc = reader.readOperationalData(ncInstanceId)
-        if(nc == null) {
-            return null;
-        }
-        return nc.toADNodeConnectorProperties     
-    }
-    
-    public static def toADNodeId(NodeId nodeId) {
-        checkNotNull(nodeId);
-        return nodeId.value
-    }
-    public static def toADNodeConnector(TpId source,NodeId nodeId) throws ConstructionException {
-        checkNotNull(source);
-        return new NodeConnector(MD_SAL_TYPE,source.toADNodeConnectorId,nodeId.toADNode)
-    }
-    
-    public static def toADNodeConnectorId(TpId nodeConnectorId) {
-        return nodeConnectorId.value
-    }
-    
-    public static def toADNode(NodeId nodeId) {
-        checkNotNull(nodeId);
-        return new Node(MD_SAL_TYPE,nodeId.toADNodeId);       
-    }
-}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.java
new file mode 100644 (file)
index 0000000..d78bce4
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.compatibility.topology;
+
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.topology.IPluginOutTopologyService;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public class TopologyProvider implements AutoCloseable{
+    private static final Logger LOG = LoggerFactory.getLogger(TopologyProvider.class);
+    private static final InstanceIdentifier<Link> PATH = InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class ,new TopologyKey(new TopologyId("flow:1")))
+            .child(Link.class)
+            .toInstance();
+    private TopologyCommitHandler commitHandler;
+
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private IPluginOutTopologyService topologyPublisher;
+    private DataProviderService dataService;
+
+    public void startAdapter() {
+        if(dataService == null){
+            LOG.error("dataService not set");
+            return;
+        }
+        commitHandler = new TopologyCommitHandler(dataService,topologyPublisher);
+        listenerRegistration = dataService.registerDataChangeListener(PATH, commitHandler);
+        LOG.info("TopologyProvider started");
+    }
+
+    @Override
+    public void close() {
+        if (listenerRegistration != null) {
+            listenerRegistration.close();
+        }
+    }
+
+    void setTopologyPublisher(final IPluginOutTopologyService topologyPublisher) {
+        this.topologyPublisher = topologyPublisher;
+        if (commitHandler != null) {
+            commitHandler.setTopologyPublisher(topologyPublisher);
+        }
+    }
+
+    public void setDataService(final DataProviderService dataService) {
+        this.dataService = Preconditions.checkNotNull(dataService);
+    }
+}
diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.xtend b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/topology/TopologyProvider.xtend
deleted file mode 100644 (file)
index 21f2b35..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.compatibility.topology
-
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService
-import org.opendaylight.controller.sal.topology.IPluginOutTopologyService
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey
-import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link
-import org.slf4j.LoggerFactory
-import org.opendaylight.yangtools.concepts.ListenerRegistration
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener
-
-class TopologyProvider implements AutoCloseable{
-    static val LOG = LoggerFactory.getLogger(TopologyProvider);
-    TopologyCommitHandler commitHandler
-    
-    @Property
-    IPluginOutTopologyService topologyPublisher;
-    
-    @Property
-    DataProviderService dataService;
-    
-    ListenerRegistration<DataChangeListener> listenerRegistration
-    
-    
-    def void start() {
-
-    }
-    def void startAdapter() {
-        if(dataService == null){
-            LOG.error("dataService not set");
-            return;
-        }
-        commitHandler = new TopologyCommitHandler(dataService,topologyPublisher);
-        val InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(NetworkTopology)
-            .child(Topology,new TopologyKey(new TopologyId("flow:1")))
-            .child(Link)
-            .toInstance();
-        listenerRegistration = dataService.registerDataChangeListener(path,commitHandler);
-        LOG.info("TopologyProvider started")
-    }
-    
-    override close() throws Exception {
-        listenerRegistration.close
-    }
-    
-    def setTopologyPublisher(IPluginOutTopologyService topologyPublisher) {
-        _topologyPublisher = topologyPublisher;
-        if(commitHandler != null){
-            commitHandler.setTopologyPublisher(topologyPublisher);
-        }
-    }
-    
-}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.java
new file mode 100644 (file)
index 0000000..22b6f6f
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * 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.binding.codegen;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public final class RuntimeCodeSpecification {
+    public final static String DIRECT_PROXY_SUFFIX = "DirectProxy";
+    public final static String INVOKER_SUFFIX = "ListenerInvoker";
+    public final static String ROUTER_SUFFIX = "Router";
+
+    public final static String DELEGATE_FIELD = "_delegate";
+    public final static String ROUTING_TABLE_FIELD_PREFIX = "_routes_";
+
+    private RuntimeCodeSpecification() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    /**
+     * Returns a name for generated interface
+     */
+    private static String getGeneratedName(final Class<? extends Object> cls, final String suffix) {
+        return cls.getName() + "$$Broker$" + suffix;
+    }
+
+    public static String getInvokerName(final Class<? extends NotificationListener> listener) {
+        return getGeneratedName(listener, RuntimeCodeSpecification.INVOKER_SUFFIX);
+    }
+
+    /**
+     * Returns a name for DirectProxy implementation
+     */
+    public static String getDirectProxyName(final Class<? extends RpcService> base) {
+        return getGeneratedName(base, RuntimeCodeSpecification.DIRECT_PROXY_SUFFIX);
+    }
+
+    /**
+     * Returns a name for Router implementation
+     */
+    public static String getRouterName(final Class<? extends RpcService> base) {
+        return getGeneratedName(base, RuntimeCodeSpecification.ROUTER_SUFFIX);
+    }
+
+    /**
+     * Returns a field name for specified routing context
+     */
+    public static String getRoutingTableField(final Class<? extends BaseIdentity> routingContext) {
+        return "_routes_" + routingContext.getSimpleName();
+    }
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/RuntimeCodeSpecification.xtend
deleted file mode 100644 (file)
index c5648ad..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.binding.codegen
-
-import org.opendaylight.yangtools.yang.binding.RpcService
-import org.opendaylight.yangtools.yang.binding.BaseIdentity
-import org.opendaylight.yangtools.yang.binding.NotificationListener
-
-/**
- * 
- * 
- */
-class RuntimeCodeSpecification {
-
-    //public static val PACKAGE_PREFIX = "_gen.";
-
-    public static val DIRECT_PROXY_SUFFIX = "DirectProxy";
-    public static val ROUTER_SUFFIX = "Router";
-    public static val INVOKER_SUFFIX = "ListenerInvoker";
-
-    public static val DELEGATE_FIELD = "_delegate"
-    public static val ROUTING_TABLE_FIELD_PREFIX = "_routes_"
-
-    public static def getInvokerName(Class<? extends NotificationListener> listener) {
-        getGeneratedName(listener, INVOKER_SUFFIX);
-    }
-
-    /**
-     * Returns a name for DirectProxy implementation
-     * 
-     * 
-     */
-    public static def getDirectProxyName(Class<? extends RpcService> base) {
-        getGeneratedName(base, DIRECT_PROXY_SUFFIX);
-    }
-
-    /**
-     * Returns a name for Router implementation
-     * 
-     */
-    public static def getRouterName(Class<? extends RpcService> base) {
-        getGeneratedName(base, ROUTER_SUFFIX);
-    }
-
-    /**
-     * Returns a name for generated interface
-     * 
-     */
-    public static def getGeneratedName(Class<?> cls, String suffix) {
-        '''«cls.name»$$Broker$«suffix»'''.toString()
-    }
-
-    /**
-     * Returns a field name for specified routing context
-     * 
-     */
-    public static def getRoutingTableField(Class<? extends BaseIdentity> routingContext) {
-        return '''_routes_«routingContext.simpleName»'''.toString;
-    }
-}
index 0bc11d9b3547298d321dee112014963f3ff676d2..4ad0bb0558a37bcdd6827fdb0a63c1d047553bba 100644 (file)
@@ -5,18 +5,19 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-
-
 package org.opendaylight.controller.sal.binding.codegen;
 
 import java.lang.reflect.Method;
+
 import org.opendaylight.yangtools.yang.binding.Notification;
 
-@SuppressWarnings("all")
-public class YangtoolsMappingHelper {
-  public static boolean isNotificationCallback(final Method it) {
-      return it.getName().startsWith("on") && (it.getParameterTypes().length == 1) &&
-              Notification.class.isAssignableFrom(it.getParameterTypes()[0]);
-  }
+public final class YangtoolsMappingHelper {
+    private YangtoolsMappingHelper() {
+        throw new UnsupportedOperationException("Utility class");
+    }
 
+    public static boolean isNotificationCallback(final Method it) {
+        return it.getName().startsWith("on") && (it.getParameterTypes().length == 1) &&
+                Notification.class.isAssignableFrom(it.getParameterTypes()[0]);
+    }
 }
\ No newline at end of file
index 57b4960145b7560c3e19e7dae0d8e2e578e8b43d..a15c711e2d2eb88c0a5647baaaa4bdc168a91cac 100644 (file)
@@ -27,6 +27,7 @@ public class TestHelper {
     public static Option mdSalCoreBundles() {
         return new DefaultCompositeOption( //
                 mavenBundle(YANGTOOLS, "concepts").versionAsInProject(), // //
+                mavenBundle(YANGTOOLS, "util").versionAsInProject(), // //
                 mavenBundle(YANGTOOLS, "yang-binding").versionAsInProject(), // //
                 mavenBundle(YANGTOOLS, "yang-common").versionAsInProject(), // //
                 mavenBundle(CONTROLLER, "sal-common").versionAsInProject(), // //
index 162b66bda18c6bb6252c73d5046997ba750f2996..bac9146bf5395f29a7ce9c8fa733802b0cf42259 100644 (file)
   <packaging>bundle</packaging>
 
   <dependencies>
+    <dependency>
+      <groupId>com.github.romix</groupId>
+      <artifactId>java-concurrent-hash-trie-map</artifactId>
+    </dependency>
     <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.xtend</groupId>
-      <artifactId>org.eclipse.xtend.lib</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>config-api</artifactId>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-core-spi</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>util</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-impl</artifactId>
@@ -50,6 +54,7 @@
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-parser-impl</artifactId>
     </dependency>
+
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
                             org.opendaylight.yangtools.yang.util,
                             org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.dom.impl.rev131028.*</Private-Package>
             <Import-Package>*</Import-Package>
+            <Embed-Dependency>java-concurrent-hash-trie-map;inline=true</Embed-Dependency>
           </instructions>
         </configuration>
       </plugin>
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
       <!-- TODO - unite yang-maven-plugin configuration in md-sal-->
       <plugin>
         <groupId>org.opendaylight.yangtools</groupId>
index 93284c98e1032f0030fff3c21d2c6c3dfc2ba1f2..fd24944018c69b3b9480a7018e0fea1c23986719 100644 (file)
@@ -19,29 +19,26 @@ import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
-*
-*/
 public final class SchemaServiceImplSingletonModule extends
-        org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractSchemaServiceImplSingletonModule {
+org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractSchemaServiceImplSingletonModule {
 
     private static final Logger LOG = LoggerFactory.getLogger(SchemaServiceImplSingletonModule.class);
 
     BundleContext bundleContext;
 
-    public SchemaServiceImplSingletonModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
-            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+    public SchemaServiceImplSingletonModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public SchemaServiceImplSingletonModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
-            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
-            SchemaServiceImplSingletonModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public SchemaServiceImplSingletonModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            final SchemaServiceImplSingletonModule oldModule, final java.lang.AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
     @Override
-    public boolean canReuseInstance(AbstractSchemaServiceImplSingletonModule oldModule) {
+    public boolean canReuseInstance(final AbstractSchemaServiceImplSingletonModule oldModule) {
         return true;
     }
 
@@ -49,7 +46,7 @@ public final class SchemaServiceImplSingletonModule extends
         return bundleContext;
     }
 
-    public void setBundleContext(BundleContext bundleContext) {
+    public void setBundleContext(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
     }
 
@@ -65,8 +62,7 @@ public final class SchemaServiceImplSingletonModule extends
             return new GlobalSchemaServiceProxy(getBundleContext(), ref);
         }
 
-        GlobalBundleScanningSchemaServiceImpl newInstance = new GlobalBundleScanningSchemaServiceImpl();
-        newInstance.setContext(getBundleContext());
+        GlobalBundleScanningSchemaServiceImpl newInstance = new GlobalBundleScanningSchemaServiceImpl(getBundleContext());
         newInstance.start();
         return newInstance;
     }
@@ -77,7 +73,7 @@ public final class SchemaServiceImplSingletonModule extends
         private ServiceReference<SchemaService> reference;
         private SchemaService delegate;
 
-        public GlobalSchemaServiceProxy(BundleContext bundleContext, ServiceReference<SchemaService> ref) {
+        public GlobalSchemaServiceProxy(final BundleContext bundleContext, final ServiceReference<SchemaService> ref) {
             this.bundleContext = bundleContext;
             this.reference = ref;
             this.delegate = bundleContext.getService(reference);
@@ -102,7 +98,7 @@ public final class SchemaServiceImplSingletonModule extends
         }
 
         @Override
-        public void addModule(Module arg0) {
+        public void addModule(final Module arg0) {
             delegate.addModule(arg0);
         }
 
@@ -117,12 +113,12 @@ public final class SchemaServiceImplSingletonModule extends
         }
 
         @Override
-        public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener arg0) {
+        public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener arg0) {
             return delegate.registerSchemaServiceListener(arg0);
         }
 
         @Override
-        public void removeModule(Module arg0) {
+        public void removeModule(final Module arg0) {
             delegate.removeModule(arg0);
         }
 
index d04be6b24423a83e13d2cb3366384c9d15b459eb..87c68596efa60a668a7afe005d482eefac1f6ad7 100644 (file)
@@ -263,7 +263,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable<String>, Sch
             ready = true;
 
             LOG.debug("Store transaction: {} : Ready", getIdentifier());
-            mutableTree.seal();
+            mutableTree.ready();
             return store.submit(this);
         }
 
index b36ef3dd7c16250bfcc101ab4567be3feaaadbc4..2a163d8dbc7334c95a24481d1a1835ffa8496586 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
 import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder;
-import static org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils.append;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -46,23 +45,10 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 
 /**
- *
  * Resolve Data Change Events based on modifications and listeners
  *
  * Computes data change events for all affected registered listeners in data
  * tree.
- *
- * Prerequisites for computation is to set all parameters properly:
- * <ul>
- * <li>{@link #setRootPath(InstanceIdentifier)} - Root path of datastore
- * <li>{@link #setListenerRoot(ListenerTree)} - Root of listener registration
- * tree, which contains listeners to be notified
- * <li>{@link #setModificationRoot(NodeModification)} - Modification root, for
- * which events should be computed
- * <li>{@link #setBeforeRoot(Optional)} - State of before modification occurred
- * <li>{@link #setAfterRoot(Optional)} - State of after modification occurred
- * </ul>
- *
  */
 final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListenerNotifyTask>> {
     private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class);
@@ -72,7 +58,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
     private final DataTreeCandidate candidate;
     private final ListenerTree listenerRoot;
 
-    public ResolveDataChangeEventsTask(DataTreeCandidate candidate, ListenerTree listenerTree) {
+    public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
         this.candidate = Preconditions.checkNotNull(candidate);
         this.listenerRoot = Preconditions.checkNotNull(listenerTree);
     }
@@ -83,7 +69,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
      * Implementation of done as Map-Reduce with two steps: 1. resolving events
      * and their mapping to listeners 2. merging events affecting same listener
      *
-     * @return Iterable of Notification Tasks which needs to be executed in
+     * @return An {@link Iterable} of Notification Tasks which needs to be executed in
      *         order to delivery data change events.
      */
     @Override
@@ -329,7 +315,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
         for (NormalizedNode<PathArgument, ?> beforeChild : beforeCont.getValue()) {
             PathArgument childId = beforeChild.getIdentifier();
             alreadyProcessed.add(childId);
-            InstanceIdentifier childPath = append(path, childId);
+            InstanceIdentifier childPath = path.node(childId);
             Collection<ListenerTree.Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
             Optional<NormalizedNode<PathArgument, ?>> afterChild = afterCont.getChild(childId);
             DOMImmutableDataChangeEvent childChange = resolveNodeContainerChildUpdated(childPath, childListeners,
@@ -348,7 +334,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
                 // and it was not present in previous loop, that means it is
                 // created.
                 Collection<ListenerTree.Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
-                InstanceIdentifier childPath = append(path,childId);
+                InstanceIdentifier childPath = path.node(childId);
                 childChanges.add(resolveSameEventRecursivelly(childPath , childListeners, afterChild,
                         DOMImmutableDataChangeEvent.getCreateEventFactory()));
             }
@@ -432,7 +418,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
                 PathArgument childId = child.getIdentifier();
                 LOG.trace("Resolving event for child {}", childId);
                 Collection<Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
-                eventBuilder.merge(resolveSameEventRecursivelly(append(path, childId), childListeners, child, eventFactory));
+                eventBuilder.merge(resolveSameEventRecursivelly(path.node(childId), childListeners, child, eventFactory));
             }
             propagateEvent = eventBuilder.build();
         } else {
@@ -460,7 +446,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
 
         for (DataTreeCandidateNode childMod : modification.getChildNodes()) {
             PathArgument childId = childMod.getIdentifier();
-            InstanceIdentifier childPath = append(path, childId);
+            InstanceIdentifier childPath = path.node(childId);
             Collection<ListenerTree.Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
 
             switch (childMod.getModificationType()) {
@@ -522,7 +508,7 @@ final class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeListe
         }
     }
 
-    public static ResolveDataChangeEventsTask create(DataTreeCandidate candidate, ListenerTree listenerTree) {
+    public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
         return new ResolveDataChangeEventsTask(candidate, listenerTree);
     }
 }
index bb2111e6a08882b71656e28c96758ccfe19f178e..bd24ac5fcfd3c670b2677531eb485cba3a9e7d4c 100644 (file)
@@ -11,20 +11,42 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 
 import com.google.common.base.Preconditions;
 
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the datastore. This can have multiple reasons, for example
+ * the datastore has been concurrently modified such that a conflicting
+ * node is present, or the modification is structurally incorrect.
+ */
 public class DataPreconditionFailedException extends Exception {
     private static final long serialVersionUID = 1L;
     private final InstanceIdentifier path;
 
+    /**
+     * Create a new instance.
+     *
+     * @param path Object path which caused this exception
+     * @param message Specific message describing the failure
+     */
     public DataPreconditionFailedException(final InstanceIdentifier path, final String message) {
-        super(message);
-        this.path = Preconditions.checkNotNull(path);
+        this(path, message, null);
     }
-
+    /**
+     * Create a new instance, initializing
+     *
+     * @param path Object path which caused this exception
+     * @param message Specific message describing the failure
+     * @param cause Exception which triggered this failure, may be null
+     */
     public DataPreconditionFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
         super(message, cause);
         this.path = Preconditions.checkNotNull(path);
     }
 
+    /**
+     * Returns the offending object path.
+     *
+     * @return Path of the offending object
+     */
     public InstanceIdentifier getPath() {
         return path;
     }
index 906e2844291fc502bae2a2534fe71bf1cb1d02a6..d860dfc064cbbbb6420b39dc47840ad3f78040bf 100644 (file)
@@ -9,7 +9,27 @@ package org.opendaylight.controller.md.sal.dom.store.impl.tree;
 
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 
+/**
+ * An encapsulation of a validated data tree modification. This candidate
+ * is ready for atomic commit to the datastore. It allows access to before-
+ * and after-state as it will be seen in to subsequent commit. This capture
+ * can be accessed for reference, but cannot be modified and the content
+ * is limited to nodes which were affected by the modification from which
+ * this instance originated.
+ */
 public interface DataTreeCandidate {
+    /**
+     * Get the candidate tree root node.
+     *
+     * @return Candidate tree root node
+     */
     DataTreeCandidateNode getRootNode();
+
+    /**
+     * Get the candidate tree root path. This is the path of the root node
+     * relative to the root of InstanceIdentifier namespace.
+     *
+     * @return Relative path of the root node
+     */
     InstanceIdentifier getRootPath();
 }
index b1ca45b53e3320e18a6ce7bdc761420e01c4b033..528419d235033ec8d85d43cfb36229bd477b13e6 100644 (file)
@@ -12,11 +12,47 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 import com.google.common.base.Optional;
 
+/**
+ * A single node within a {@link DataTreeCandidate}. The nodes are organized
+ * in tree hierarchy, reflecting the modification from which this candidate
+ * was created. The node itself exposes the before- and after-image of the
+ * tree restricted to the modified nodes.
+ */
 public interface DataTreeCandidateNode {
+    /**
+     * Get the node identifier.
+     *
+     * @return The node identifier.
+     */
     PathArgument getIdentifier();
+
+    /**
+     * Get an unmodifiable iterable of modified child nodes.
+     *
+     * @return Unmodifiable iterable of modified child nodes.
+     */
     Iterable<DataTreeCandidateNode> getChildNodes();
 
+    /**
+     * Return the type of modification this node is undergoing.
+     *
+     * @return Node modification type.
+     */
     ModificationType getModificationType();
+
+    /**
+     * Return the before-image of data corresponding to the node.
+     *
+     * @return Node data as they were present in the tree before
+     *         the modification was applied.
+     */
     Optional<NormalizedNode<?, ?>> getDataAfter();
+
+    /**
+     * Return the after-image of data corresponding to the node.
+     *
+     * @return Node data as they will be present in the tree after
+     *         the modification is applied.
+     */
     Optional<NormalizedNode<?, ?>> getDataBefore();
 }
index cff90a4aefd2079915af4fb327ae48d5ef609a29..e4370c46a04fbc2539796672abc2baf9abeeae1f 100644 (file)
@@ -16,8 +16,35 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
  * has the ability to rebase itself to a new snapshot.
  */
 public interface DataTreeModification extends DataTreeSnapshot {
-       void delete(InstanceIdentifier path);
-       void merge(InstanceIdentifier path, NormalizedNode<?, ?> data);
-       void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
-       void seal();
+    /**
+     * Delete the node at specified path.
+     *
+     * @param path Node path
+     */
+    void delete(InstanceIdentifier path);
+
+    /**
+     * Merge the specified data with the currently-present data
+     * at specified path.
+     *
+     * @param path Node path
+     * @param data Data to be merged
+     */
+    void merge(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Replace the data at specified path with supplied data.
+     *
+     * @param path Node path
+     * @param data New node data
+     */
+    void write(InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+    /**
+     * Finish creation of a modification, making it ready for application
+     * to the data tree. Any calls to this object's methods will result
+     * in undefined behavior, possibly with an
+     * {@link IllegalStateException} being thrown.
+     */
+    void ready();
 }
index f93f40a9cc1f5b9bd85e00543be0497fde16d4de..0d73143de2aad021ee17f7577d95eaabdc0af24e 100644 (file)
@@ -33,15 +33,24 @@ import org.slf4j.LoggerFactory;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
+/**
+ * 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.
+ */
 public final class ListenerTree {
     private static final Logger LOG = LoggerFactory.getLogger(ListenerTree.class);
     private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
     private final Node rootNode = new Node(null, null);
 
     private ListenerTree() {
-
+        // Private to disallow direct instantiation
     }
 
+    /**
+     * Create a new empty instance of the listener tree.
+     *
+     * @return An empty instance.
+     */
     public static ListenerTree create() {
         return new ListenerTree();
     }
@@ -110,6 +119,14 @@ public final class ListenerTree {
         }
     }
 
+    /**
+     * Obtain a tree walking context. This context ensures a consistent view of
+     * the listener registrations. The context should be closed as soon as it
+     * is not required, because each unclosed instance blocks modification of
+     * the listener tree.
+     *
+     * @return A walker instance.
+     */
     public Walker getWalker() {
         /*
          * TODO: The only current user of this method is local to the datastore.
@@ -124,6 +141,10 @@ public final class ListenerTree {
         return ret;
     }
 
+    /**
+     * A walking context, pretty much equivalent to an iterator, but it
+     * exposes the undelying tree structure.
+     */
     public static final class Walker implements AutoCloseable {
         private final Lock lock;
         private final Node node;
index b16e907120ac098ee81a3f9fec4dd83469971f69..b9a26f5c00be0b6cf45775e141d22728b1d22ee2 100644 (file)
@@ -7,37 +7,39 @@
  */
 package org.opendaylight.controller.md.sal.dom.store.impl.tree;
 
+/**
+ * Enumeration of all possible node modification states. These are used in
+ * data tree modification context to quickly assess what sort of modification
+ * the node is undergoing.
+ */
 public enum ModificationType {
-
     /**
-     *
-     * Node is unmodified
-     *
-     *
+     * Node is currently unmodified.
      */
     UNMODIFIED,
+
     /**
-     *
-     * Child of tree node was modified
-     *
+     * A child node, either direct or indirect, has been modified. This means
+     * that the data representation of this node has potentially changed.
      */
     SUBTREE_MODIFIED,
+
     /**
-     * Tree node was replaced with new value / subtree
-     *
+     * This node has been placed into the tree, potentially completely replacing
+     * pre-existing contents.
      */
     WRITE,
+
     /**
-     *
-     * Tree node is to be deleted.
-     *
+     * This node has been deleted along with any of its child nodes.
      */
     DELETE,
 
     /**
-     *
-     * Tree node is to be merged with existing one.
-     *
+     * Node has been written into the tree, but instead of replacing pre-existing
+     * contents, it has been merged. This means that any incoming nodes which
+     * were present in the tree have been replaced, but their child nodes have
+     * been retained.
      */
-    MERGE
+    MERGE,
 }
index 52beaa7c616d046ea9793aa712b37f860c2d03cc..d714f1cc85449d60967c0d87cc05c4282ec3d880 100644 (file)
@@ -10,17 +10,17 @@ package org.opendaylight.controller.md.sal.dom.store.impl.tree;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 
 import com.google.common.base.Optional;
+
 /**
- *
- * Tree node which contains references to it's leafs
+ * A tree node which has references to its child leaves. This are typically
+ * internal non-data leaves, such as containers, lists, etc.
  *
  * @param <C> Final node type
  */
 public interface StoreTreeNode<C extends StoreTreeNode<C>> {
 
     /**
-     *
-     * Returns direct child of the node
+     * Returns a direct child of the node
      *
      * @param child Identifier of child
      * @return Optional with node if the child is existing, {@link Optional#absent()} otherwise.
index 7e783f927d95d9be9f92041074cf6769ef582bac..b634866856b11e810c17bba036bece830a887ba5 100644 (file)
@@ -7,60 +7,32 @@
  */
 package org.opendaylight.controller.md.sal.dom.store.impl.tree;
 
-import java.util.Set;
-
-import org.opendaylight.yangtools.concepts.Identifiable;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
 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.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
 
-import com.google.common.base.Function;
 import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.primitives.UnsignedLong;
 
+/**
+ * Data store tree manipulation utilities.
+ */
 public final class StoreUtils {
     private static final int STRINGTREE_INDENT = 4;
 
-    private final static Function<Identifiable<Object>, Object> EXTRACT_IDENTIFIER = new Function<Identifiable<Object>, Object>() {
-        @Override
-        public Object apply(final Identifiable<Object> input) {
-            return input.getIdentifier();
-        }
-    };
-
     private StoreUtils() {
         throw new UnsupportedOperationException("Utility class should not be instantiated");
     }
 
-    /*
-     * Suppressing warnings here allows us to fool the compiler enough
-     * such that we can reuse a single function for all applicable types
-     * and present it in a type-safe manner to our users.
+    /**
+     * Convert a data subtree under a node into a human-readable string format.
+     *
+     * @param node Data subtree root
+     * @return String containing a human-readable form of the subtree.
      */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static <V> Function<Identifiable<V>, V> identifierExtractor() {
-        return (Function) EXTRACT_IDENTIFIER;
-    }
-
-    public static final UnsignedLong increase(final UnsignedLong original) {
-        return original.plus(UnsignedLong.ONE);
-    }
-
-    public static final InstanceIdentifier append(final InstanceIdentifier parent, final PathArgument arg) {
-        return new InstanceIdentifier(ImmutableList.<PathArgument> builder().addAll(parent.getPath()).add(arg).build());
-    }
-
-    public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
-        return FluentIterable.from(children).transform(StoreUtils.<V> identifierExtractor()).toSet();
-    }
-
     public static String toStringTree(final NormalizedNode<?, ?> node) {
-        StringBuilder builder = new StringBuilder();
+        final StringBuilder builder = new StringBuilder();
         toStringTree(builder, node, 0);
         return builder.toString();
     }
index 732352dd34bbf974b6d897984d9b731c72e5e591..99982d318cf5487699d2db60549e4ee194ed0540 100644 (file)
@@ -13,6 +13,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 
@@ -21,6 +22,9 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 
+/**
+ * A set of utility methods for interacting with {@link TreeNode} objects.
+ */
 public final class TreeNodeUtils {
     private TreeNodeUtils() {
         throw new UnsupportedOperationException("Utility class should not be instantiated");
@@ -32,7 +36,6 @@ public final class TreeNodeUtils {
      * @param tree Data Tree
      * @param path Path to the node
      * @return Optional with node if the node is present in tree, {@link Optional#absent()} otherwise.
-     *
      */
     public static <T extends StoreTreeNode<T>> Optional<T> findNode(final T tree, final InstanceIdentifier path) {
         Optional<T> current = Optional.<T> of(tree);
index 15479be462ac6580ddc50f2ec19de45ae0b1fd45..c09a1a38b590891444cb69baa274fc6e7b52ce85 100644 (file)
@@ -16,7 +16,7 @@ import com.google.common.base.Optional;
 final class AlwaysFailOperation implements ModificationApplyOperation {
     @Override
     public Optional<TreeNode> apply(final ModifiedNode modification,
-            final Optional<TreeNode> storeMeta, final Version subtreeVersion) {
+            final Optional<TreeNode> storeMeta, final Version version) {
         throw new IllegalStateException("Schema Context is not available.");
     }
 
index c05ed4b442d0fa964fa94d88fb9526a5c88673c6..f7e95b84bd4df38c214c32f10c1b01df61fb4798 100644 (file)
@@ -131,7 +131,7 @@ final class InMemoryDataTreeModification implements DataTreeModification {
     }
 
     @Override
-    public synchronized void seal() {
+    public synchronized void ready() {
         Preconditions.checkState(!sealed, "Attempted to seal an already-sealed Data Tree.");
         sealed = true;
         rootNode.seal();
index 10f39a8cefa045bfce6b356b63d3c1c0b3782c4f..df03db35bec44bc6216cdfe760c0853ce6cda141 100644 (file)
@@ -50,7 +50,7 @@ interface ModificationApplyOperation extends StoreTreeNode<ModificationApplyOper
      * @param storeMeta
      *            Store Metadata Node on which NodeModification should be
      *            applied
-     * @param subtreeVersion New subtree version of parent node
+     * @param version New subtree version of parent node
      * @throws IllegalArgumentException
      *             If it is not possible to apply Operation on provided Metadata
      *             node
@@ -58,7 +58,7 @@ interface ModificationApplyOperation extends StoreTreeNode<ModificationApplyOper
      *         node, {@link Optional#absent()} if {@link ModifiedNode}
      *         resulted in deletion of this node.
      */
-    Optional<TreeNode> apply(ModifiedNode modification, Optional<TreeNode> storeMeta, Version subtreeVersion);
+    Optional<TreeNode> apply(ModifiedNode modification, Optional<TreeNode> storeMeta, Version version);
 
     /**
      *
index 2639d050efe28b8b0903636e987b5c134e79c922..e7e79f891687592db4ac45a14eb558dae20c085b 100644 (file)
@@ -14,8 +14,31 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 
 import com.google.common.base.Optional;
 
+/**
+ * Internal interface representing a modification action of a particular node.
+ * It is used by the validation code to allow for a read-only view of the
+ * modification tree as we should never modify that during validation.
+ */
 interface NodeModification extends Identifiable<PathArgument> {
+    /**
+     * Get the type of modification.
+     *
+     * @return Modification type.
+     */
     ModificationType getType();
+
+    /**
+     * Get the original tree node to which the modification is to be applied.
+     *
+     * @return The original node, or {@link Optional#absent()} if the node is
+     *         a new node.
+     */
     Optional<TreeNode> getOriginal();
+
+    /**
+     * Get a read-only view of children nodes.
+     *
+     * @return Iterable of all children nodes.
+     */
     Iterable<? extends NodeModification> getChildren();
 }
index 2ef85cbcb7e548cb6fa1ccd932e7dddbb651ace3..227684ae35ee1f6bdbc8685df670c9cc157881af 100644 (file)
@@ -18,6 +18,11 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 
+/**
+ * Internal utility class for an empty candidate. We instantiate this class
+ * for empty modifications, saving memory and processing speed. Instances
+ * of this class are explicitly recognized and processing of them is skipped.
+ */
 final class NoopDataTreeCandidate extends AbstractDataTreeCandidate {
     private static final DataTreeCandidateNode ROOT = new DataTreeCandidateNode() {
         @Override
index 3a3af5ecab966049196c576df7f6c7bd24b560a2..5c6aeace569aabd1020a1b67e4572e428dbf2da1 100644 (file)
@@ -13,7 +13,6 @@ import java.util.Map;
 
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.MutableTreeNode;
@@ -95,16 +94,9 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
 
     @Override
     protected TreeNode applyWrite(final ModifiedNode modification,
-            final Optional<TreeNode> currentMeta, final Version subtreeVersion) {
-        final Version nodeVersion;
-        if (currentMeta.isPresent()) {
-            nodeVersion = currentMeta.get().getVersion().next();
-        } else {
-            nodeVersion = subtreeVersion;
-        }
-
+            final Optional<TreeNode> currentMeta, final Version version) {
         final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
-        final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, nodeVersion);
+        final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
 
         if (Iterables.isEmpty(modification.getChildren())) {
             return newValueMeta;
@@ -122,12 +114,12 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
          * and run the common parts on it -- which end with the node being sealed.
          */
         final MutableTreeNode mutable = newValueMeta.mutable();
-        mutable.setSubtreeVersion(subtreeVersion);
+        mutable.setSubtreeVersion(version);
 
         @SuppressWarnings("rawtypes")
         final NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
 
-        return mutateChildren(mutable, dataBuilder, nodeVersion, modification.getChildren());
+        return mutateChildren(mutable, dataBuilder, version, modification.getChildren());
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -155,24 +147,21 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
 
     @Override
     protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
-            final Version subtreeVersion) {
+            final Version version) {
         // For Node Containers - merge is same as subtree change - we only replace children.
-        return applySubtreeChange(modification, currentMeta, subtreeVersion);
+        return applySubtreeChange(modification, currentMeta, version);
     }
 
     @Override
     public TreeNode applySubtreeChange(final ModifiedNode modification,
-            final TreeNode currentMeta, final Version subtreeVersion) {
-        // Bump subtree version to its new target
-        final Version updatedSubtreeVersion = currentMeta.getSubtreeVersion().next();
-
+            final TreeNode currentMeta, final Version version) {
         final MutableTreeNode newMeta = currentMeta.mutable();
-        newMeta.setSubtreeVersion(updatedSubtreeVersion);
+        newMeta.setSubtreeVersion(version);
 
         @SuppressWarnings("rawtypes")
         NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
 
-        return mutateChildren(newMeta, dataBuilder, updatedSubtreeVersion, modification.getChildren());
+        return mutateChildren(newMeta, dataBuilder, version, modification.getChildren());
     }
 
     @Override
@@ -188,7 +177,7 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
             final PathArgument childId = childMod.getIdentifier();
             final Optional<TreeNode> childMeta = currentMeta.getChild(childId);
 
-            InstanceIdentifier childPath = StoreUtils.append(path, childId);
+            InstanceIdentifier childPath = path.node(childId);
             resolveChildOperation(childId).checkApplicable(childPath, childMod, childMeta);
         }
     }
index ff90d57f49795ef0927e5c7bd00f89684078d004..e1cc1a17e5a933832ec3527d3fe2fb5ab929c906 100644 (file)
@@ -44,8 +44,8 @@ final class OperationWithModification {
         return applyOperation;
     }
 
-    public Optional<TreeNode> apply(final Optional<TreeNode> data, final Version subtreeVersion) {
-        return applyOperation.apply(modification, data, subtreeVersion);
+    public Optional<TreeNode> apply(final Optional<TreeNode> data, final Version version) {
+        return applyOperation.apply(modification, data, version);
     }
 
     public static OperationWithModification from(final ModificationApplyOperation operation,
index bdf5667b67d92568c5b538d294ee417aabbe76df..6ef76adacf9cc478f36b5c9914b9704b16ffd05c 100644 (file)
@@ -184,7 +184,7 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
 
     @Override
     public final Optional<TreeNode> apply(final ModifiedNode modification,
-            final Optional<TreeNode> currentMeta, final Version subtreeVersion) {
+            final Optional<TreeNode> currentMeta, final Version version) {
 
         switch (modification.getType()) {
         case DELETE:
@@ -193,13 +193,13 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
             Preconditions.checkArgument(currentMeta.isPresent(), "Metadata not available for modification",
                     modification);
             return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(),
-                    subtreeVersion)));
+                    version)));
         case MERGE:
             if(currentMeta.isPresent()) {
-                return modification.storeSnapshot(Optional.of(applyMerge(modification,currentMeta.get(),subtreeVersion)));
+                return modification.storeSnapshot(Optional.of(applyMerge(modification,currentMeta.get(), version)));
             } // Fallback to write is intentional - if node is not preexisting merge is same as write
         case WRITE:
-            return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
+            return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, version)));
         case UNMODIFIED:
             return currentMeta;
         default:
@@ -208,13 +208,13 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
     }
 
     protected abstract TreeNode applyMerge(ModifiedNode modification,
-            TreeNode currentMeta, Version subtreeVersion);
+            TreeNode currentMeta, Version version);
 
     protected abstract TreeNode applyWrite(ModifiedNode modification,
-            Optional<TreeNode> currentMeta, Version subtreeVersion);
+            Optional<TreeNode> currentMeta, Version version);
 
     protected abstract TreeNode applySubtreeChange(ModifiedNode modification,
-            TreeNode currentMeta, Version subtreeVersion);
+            TreeNode currentMeta, Version version);
 
     protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path, final NodeModification modification,
             final Optional<TreeNode> current) throws DataPreconditionFailedException;
@@ -231,20 +231,20 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
 
         @Override
         protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
-                final Version subtreeVersion) {
-            return applyWrite(modification, Optional.of(currentMeta), subtreeVersion);
+                final Version version) {
+            return applyWrite(modification, Optional.of(currentMeta), version);
         }
 
         @Override
         protected TreeNode applySubtreeChange(final ModifiedNode modification,
-                final TreeNode currentMeta, final Version subtreeVersion) {
+                final TreeNode currentMeta, final Version version) {
             throw new UnsupportedOperationException("UnkeyedList does not support subtree change.");
         }
 
         @Override
         protected TreeNode applyWrite(final ModifiedNode modification,
-                final Optional<TreeNode> currentMeta, final Version subtreeVersion) {
-            return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), subtreeVersion);
+                final Optional<TreeNode> currentMeta, final Version version) {
+            return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), version);
         }
 
         @Override
index 5f68782a2e70a309abfff0ff2b99c5a1321b6fce..a9f69ac953afdfda5d853e47474d09efe0a26021 100644 (file)
@@ -48,22 +48,22 @@ abstract class ValueNodeModificationStrategy<T extends DataSchemaNode> extends S
 
     @Override
     protected TreeNode applySubtreeChange(final ModifiedNode modification,
-            final TreeNode currentMeta, final Version subtreeVersion) {
+            final TreeNode currentMeta, final Version version) {
         throw new UnsupportedOperationException("Node " + schema.getPath()
                 + "is leaf type node. Subtree change is not allowed.");
     }
 
     @Override
     protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
-            final Version subtreeVersion) {
+            final Version version) {
         // Just overwrite whatever was there
-        return applyWrite(modification, null, subtreeVersion);
+        return applyWrite(modification, null, version);
     }
 
     @Override
     protected TreeNode applyWrite(final ModifiedNode modification,
-            final Optional<TreeNode> currentMeta, final Version subtreeVersion) {
-        return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), subtreeVersion);
+            final Optional<TreeNode> currentMeta, final Version version) {
+        return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), version);
     }
 
     @Override
index 1444f0c6a859c43bfc39c39c8b1ad26e3374bc63..522bf3c84dd350344ad4ac65defadf5f1181d5e7 100644 (file)
@@ -12,7 +12,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 import com.google.common.base.Preconditions;
 
-/*
+/**
  * A very basic data tree node.
  */
 abstract class AbstractTreeNode implements TreeNode {
index 8f74f60498aeb515435e65b5fb4d5dd3725d9be2..3ca8db2405e44866d13f3e35adcf40b61e6d7381 100644 (file)
@@ -7,10 +7,10 @@
  */
 package org.opendaylight.controller.md.sal.dom.store.impl.tree.spi;
 
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.opendaylight.yangtools.util.MapAdaptor;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
@@ -50,14 +50,14 @@ final class ContainerNode extends AbstractTreeNode {
     }
 
     private static final class Mutable implements MutableTreeNode {
-        private final Map<PathArgument, TreeNode> children;
         private final Version version;
+        private Map<PathArgument, TreeNode> children;
         private NormalizedNode<?, ?> data;
         private Version subtreeVersion;
 
         private Mutable(final ContainerNode parent) {
             this.data = parent.getData();
-            this.children = new HashMap<>(parent.children);
+            this.children = MapAdaptor.getDefaultInstance().takeSnapshot(parent.children);
             this.subtreeVersion = parent.getSubtreeVersion();
             this.version = parent.getVersion();
         }
@@ -84,15 +84,11 @@ final class ContainerNode extends AbstractTreeNode {
 
         @Override
         public TreeNode seal() {
-            final Map<PathArgument, TreeNode> realChildren;
+            final TreeNode ret = new ContainerNode(data, version, MapAdaptor.getDefaultInstance().optimize(children), subtreeVersion);
 
-            if (children.isEmpty()) {
-                realChildren = Collections.emptyMap();
-            } else {
-                realChildren = children;
-            }
-
-            return new ContainerNode(data, version, realChildren, subtreeVersion);
+            // This forces a NPE if this class is accessed again. Better than corruption.
+            children = null;
+            return ret;
         }
 
         @Override
@@ -103,8 +99,8 @@ final class ContainerNode extends AbstractTreeNode {
 
     private static ContainerNode create(final Version version, final NormalizedNode<?, ?> data,
             final Iterable<NormalizedNode<?, ?>> children) {
-        final Map<PathArgument, TreeNode> map = new HashMap<>();
 
+        final Map<PathArgument, TreeNode> map = new HashMap<>();
         for (NormalizedNode<?, ?> child : children) {
             map.put(child.getIdentifier(), TreeNodeFactory.createTreeNode(child, version));
         }
index dd3672cf113e8a785acfa434ba112079fa305f90..087f4de666155217722bca1407eb168efa069eed 100644 (file)
@@ -11,10 +11,49 @@ import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreTreeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
+/**
+ * A mutable tree node. This is a transient view materialized from a pre-existing
+ * node. Modifications are isolated. Once this object is {@link #seal()}-ed,
+ * any interactions with it will result in undefined behavior.
+ */
 public interface MutableTreeNode extends StoreTreeNode<TreeNode> {
+    /**
+     * Set the data component of the node.
+     *
+     * @param data New data component, may not be null.
+     */
     void setData(NormalizedNode<?, ?> data);
+
+    /**
+     * Set the new subtree version. This is typically invoked when the user
+     * has modified some of this node's children.
+     *
+     * @param subtreeVersion New subtree version.
+     */
     void setSubtreeVersion(Version subtreeVersion);
+
+    /**
+     * Add a new child node. This acts as add-or-replace operation, e.g. it
+     * succeeds even if a conflicting child is already present.
+     *
+     * @param child New child node.
+     */
     void addChild(TreeNode child);
+
+    /**
+     * Remove a child node. This acts as delete-or-nothing operation, e.g. it
+     * succeeds even if the corresponding child is not present.
+     *
+     * @param id Child identificator.
+     */
     void removeChild(PathArgument id);
+
+    /**
+     * Finish node modification and return a read-only view of this node. After
+     * this method is invoked, any further calls to this object's method result
+     * in undefined behavior.
+     *
+     * @return Read-only view of this node.
+     */
     TreeNode seal();
 }
index b0beb8168befede9c70bdee711ec05084997887a..def1958123c951419faeecab6d5cee5654903a3e 100644 (file)
@@ -12,7 +12,7 @@ import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
-/*
+/**
  * A very basic data tree node. It has a version (when it was last modified),
  * a subtree version (when any of its children were modified) and some read-only
  * data.
index c5d174caad9dcbe35ac81d83dcce39e7291f0364..9547628ae9565235d0519328d674953e83f54c68 100644 (file)
@@ -11,6 +11,10 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
 import org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer;
 
+/**
+ * Public entrypoint for other packages. Allows instantiating a tree node
+ * with specified version.
+ */
 public final class TreeNodeFactory {
     private TreeNodeFactory() {
         throw new UnsupportedOperationException("Utility class should not be instantiated");
index 7194faadf650a73b7cdd0cd6434592254889ec93..d89928b51e7f35dd19e2d386744a9b5fa9408bdf 100644 (file)
@@ -14,6 +14,11 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Optional;
 
+/**
+ * Concretization of AbstractTreeNode for leaf nodes which only contain data.
+ * Instances of this class report all children as absent, subtree version
+ * equal to this node's version and do not support mutable view.
+ */
 final class ValueNode extends AbstractTreeNode {
     private static final Logger LOG = LoggerFactory.getLogger(ValueNode.class);
 
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.java
new file mode 100644 (file)
index 0000000..7f83435
--- /dev/null
@@ -0,0 +1,134 @@
+/**
+ * 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;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerConfigActivator.xtend
deleted file mode 100644 (file)
index 357a516..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.ServiceRegistration
-
-class BrokerConfigActivator implements AutoCloseable {
-
-    private static val ROOT = InstanceIdentifier.builder().toInstance();
-
-    @Property
-    private var DataProviderService dataService;
-
-    private var ServiceRegistration<DataBrokerService> dataReg;
-    private var ServiceRegistration<DataProviderService> dataProviderReg;
-    private var ServiceRegistration<MountService> mountReg;
-    private var ServiceRegistration<MountProvisionService> mountProviderReg;
-    private var SchemaService schemaService;
-    private var ServiceRegistration<RpcProvisionRegistry> rpcProvisionRegistryReg;
-    private var MountPointManagerImpl mountService;
-
-    SchemaAwareDataStoreAdapter wrappedStore
-
-    public def void start(BrokerImpl broker, DataStore store, DOMDataBroker asyncBroker,BundleContext context) {
-        val emptyProperties = new Hashtable<String, String>();
-        broker.setBundleContext(context);
-
-        val serviceRef = context.getServiceReference(SchemaService);
-        schemaService = context.getService(serviceRef);
-
-        broker.setRouter(new SchemaAwareRpcBroker("/", SchemaContextProviders.fromSchemaService(schemaService)));
-        
-
-        if(asyncBroker == null) {
-            dataService = new DataBrokerImpl();
-            dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
-    
-            wrappedStore = new SchemaAwareDataStoreAdapter();
-            wrappedStore.changeDelegate(store);
-            wrappedStore.setValidationEnabled(false);
-            context.registerService(SchemaServiceListener, wrappedStore, emptyProperties)
-            
-            dataService.registerConfigurationReader(ROOT, wrappedStore);
-            dataService.registerCommitHandler(ROOT, wrappedStore);
-            dataService.registerOperationalReader(ROOT, wrappedStore);
-        } else {
-            val compatibleDataBroker = new BackwardsCompatibleDataBroker(asyncBroker);
-            context.registerService(SchemaServiceListener,compatibleDataBroker,emptyProperties);
-            dataService = compatibleDataBroker;
-        }
-        
-
-//        
-
-        mountService = new MountPointManagerImpl();
-        dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
-            mountReg = context.registerService(MountService, mountService, emptyProperties);
-        mountProviderReg = context.registerService(MountProvisionService, mountService, emptyProperties);
-
-        rpcProvisionRegistryReg = context.registerService(RpcProvisionRegistry, broker.getRouter(), emptyProperties);
-    }
-
-    override def close() {
-        dataReg?.unregister();
-        dataProviderReg?.unregister();
-        mountReg?.unregister();
-        mountProviderReg?.unregister();
-        rpcProvisionRegistryReg?.unregister();
-    }
-
-}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.java
new file mode 100644 (file)
index 0000000..e4bd034
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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 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 java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.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.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;
+
+public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable {
+    private final static Logger log = LoggerFactory.getLogger(BrokerImpl.class);
+
+    // Broker Generic Context
+    private final Set<ConsumerContextImpl> sessions = Collections
+            .synchronizedSet(new HashSet<ConsumerContextImpl>());
+    private final Set<ProviderContextImpl> providerSessions = Collections
+            .synchronizedSet(new HashSet<ProviderContextImpl>());
+
+    private BundleContext bundleContext = null;
+
+    private AutoCloseable deactivator = null;
+
+    private RpcRouter router = null;
+
+    @Override
+    public ConsumerSession registerConsumer(final Consumer consumer,
+            final BundleContext ctx) {
+        checkPredicates(consumer);
+        log.trace("Registering consumer {}", consumer);
+        final ConsumerContextImpl session = newSessionFor(consumer, ctx);
+        consumer.onSessionInitiated(session);
+        sessions.add(session);
+        return session;
+    }
+
+    @Override
+    public ProviderSession registerProvider(final Provider provider,
+            final BundleContext ctx) {
+        checkPredicates(provider);
+        final ProviderContextImpl session = newSessionFor(provider, ctx);
+        provider.onSessionInitiated(session);
+        providerSessions.add(session);
+        return session;
+    }
+
+    protected Future<RpcResult<CompositeNode>> invokeRpcAsync(final QName rpc,
+            final CompositeNode input) {
+        return router.invokeRpc(rpc, input);
+    }
+
+    // Validation
+    private void checkPredicates(final Provider prov) {
+        Preconditions.checkNotNull(prov, "Provider should not be null.");
+        for (ProviderContextImpl session : providerSessions) {
+            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()))
+                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);
+        return ret;
+    }
+
+    private ProviderContextImpl newSessionFor(final Provider provider,
+            final BundleContext ctx) {
+        ProviderContextImpl ret = new ProviderContextImpl(provider, ctx);
+        ret.setBroker(this);
+        return ret;
+    }
+
+    protected void consumerSessionClosed(
+            final ConsumerContextImpl consumerContextImpl) {
+        sessions.remove(consumerContextImpl);
+        providerSessions.remove(consumerContextImpl);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (deactivator != null) {
+            deactivator.close();
+            deactivator = null;
+        }
+    }
+
+    @Override
+    public RpcRegistration addRpcImplementation(final QName rpcType,
+            final RpcImplementation implementation)
+            throws IllegalArgumentException {
+        return router.addRpcImplementation(rpcType, implementation);
+    }
+
+    @Override
+    public RoutedRpcRegistration addRoutedRpcImplementation(
+            final QName rpcType, final RpcImplementation implementation) {
+        return router.addRoutedRpcImplementation(rpcType, implementation);
+    }
+
+    @Override
+    public void setRoutedRpcDefaultDelegate(
+            final RoutedRpcDefaultImplementation defaultImplementation) {
+        router.setRoutedRpcDefaultDelegate(defaultImplementation);
+    }
+
+    @Override
+    public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(
+            final RpcRegistrationListener listener) {
+        return router.addRpcRegistrationListener(listener);
+    }
+
+    @Override
+    public <L extends RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> ListenerRegistration<L> registerRouteChangeListener(
+            final L listener) {
+        return router.registerRouteChangeListener(listener);
+    }
+
+    @Override
+    public Set<QName> getSupportedRpcs() {
+        return router.getSupportedRpcs();
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(
+            final QName rpc, final CompositeNode input) {
+        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 AutoCloseable getDeactivator() {
+        return deactivator;
+    }
+
+    /**
+     * @param deactivator
+     *            the deactivator to set
+     */
+    public void setDeactivator(final AutoCloseable deactivator) {
+        this.deactivator = deactivator;
+    }
+
+    /**
+     * @return the router
+     */
+    public RpcRouter getRouter() {
+        return router;
+    }
+
+    /**
+     * @param router
+     *            the router to set
+     */
+    public void setRouter(final RpcRouter router) {
+        this.router = router;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BrokerImpl.xtend
deleted file mode 100644 (file)
index 0ed14c1..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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 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.Consumer
-import org.opendaylight.controller.sal.core.api.Provider
-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.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
-
-public class BrokerImpl implements Broker, RpcProvisionRegistry, AutoCloseable {
-    private static val log = LoggerFactory.getLogger(BrokerImpl);
-
-    // Broker Generic Context
-    private val Set<ConsumerContextImpl> sessions = Collections.synchronizedSet(new HashSet<ConsumerContextImpl>());
-    private val Set<ProviderContextImpl> providerSessions = Collections.synchronizedSet(
-        new HashSet<ProviderContextImpl>());
-
-    @Property
-    private var BundleContext bundleContext;
-
-    @Property
-    private var AutoCloseable deactivator;
-
-    @Property
-    private var RpcRouter router;
-
-    override registerConsumer(Consumer consumer, BundleContext ctx) {
-        checkPredicates(consumer);
-        log.trace("Registering consumer " + consumer);
-        val session = newSessionFor(consumer, ctx);
-        consumer.onSessionInitiated(session);
-        sessions.add(session);
-        return session;
-    }
-
-    override registerProvider(Provider provider, BundleContext ctx) {
-        checkPredicates(provider);
-
-        val session = newSessionFor(provider, ctx);
-        provider.onSessionInitiated(session);
-        providerSessions.add(session);
-        return session;
-    }
-
-    protected def ListenableFuture<RpcResult<CompositeNode>> invokeRpcAsync(QName rpc, CompositeNode input) {
-        return router.invokeRpc(rpc, input);
-    }
-
-    // Validation
-    private def void checkPredicates(Provider prov) {
-        if (prov == null)
-            throw new IllegalArgumentException("Provider should not be null.");
-        for (ProviderContextImpl session : providerSessions) {
-            if (prov.equals(session.getProvider()))
-                throw new IllegalStateException("Provider already registered");
-        }
-
-    }
-
-    private def void checkPredicates(Consumer cons) {
-        if (cons == null)
-            throw new IllegalArgumentException("Consumer should not be null.");
-        for (ConsumerContextImpl session : sessions) {
-            if (cons.equals(session.getConsumer()))
-                throw new IllegalStateException("Consumer already registered");
-        }
-    }
-
-    // Private Factory methods
-    private def ConsumerContextImpl newSessionFor(Consumer provider, BundleContext ctx) {
-        val ret = new ConsumerContextImpl(provider, ctx);
-        ret.broker = this;
-        return ret;
-    }
-
-    private def ProviderContextImpl newSessionFor(Provider provider, BundleContext ctx) {
-        val ret = new ProviderContextImpl(provider, ctx);
-        ret.broker = this;
-        return ret;
-    }
-
-    protected def void consumerSessionClosed(ConsumerContextImpl consumerContextImpl) {
-        sessions.remove(consumerContextImpl);
-        providerSessions.remove(consumerContextImpl);
-    }
-
-    override close() throws Exception {
-        deactivator?.close();
-    }
-
-    override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException {
-        router.addRpcImplementation(rpcType,implementation);
-    }
-
-    override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
-        router.addRoutedRpcImplementation(rpcType,implementation);
-    }
-
-    override setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultImplementation) {
-        router.setRoutedRpcDefaultDelegate(defaultImplementation);
-    }
-
-    override addRpcRegistrationListener(RpcRegistrationListener listener) {
-        return router.addRpcRegistrationListener(listener);
-    }
-
-    override <L extends RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> registerRouteChangeListener(L listener) {
-        return router.registerRouteChangeListener(listener);
-    }
-
-    override getSupportedRpcs() {
-        return router.getSupportedRpcs();
-    }
-
-    override invokeRpc(QName rpc, CompositeNode input) {
-        return router.invokeRpc(rpc,input)
-    }
-
-}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.java
new file mode 100644 (file)
index 0000000..fa81bc9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.dom.broker;
+
+import java.util.Collection;
+import java.util.concurrent.Future;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.dom.broker.osgi.AbstractBrokerServiceProxy;
+import org.opendaylight.controller.sal.dom.broker.osgi.ProxyFactory;
+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.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+
+class ConsumerContextImpl implements ConsumerSession {
+
+    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;
+    }
+
+    @Override
+    public Future<RpcResult<CompositeNode>> rpc(final QName rpc,
+            final CompositeNode input) {
+        return broker.invokeRpcAsync(rpc, input);
+    }
+
+    @Override
+    public <T extends BrokerService> T getService(final Class<T> service) {
+        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) {
+            instantiatedServices.putInstance(service, ret);
+        }
+        return ret;
+    }
+
+    @Override
+    public void close() {
+        synchronized (this) {
+            if (closed) {
+                return;
+            }
+            this.closed = true;
+        }
+
+        Collection<BrokerService> toStop = instantiatedServices.values();
+        for (BrokerService brokerService : toStop) {
+            if (brokerService instanceof AbstractBrokerServiceProxy<?>) {
+                ((AbstractBrokerServiceProxy<?>) brokerService).close();
+            }
+        }
+        broker.consumerSessionClosed(this);
+    }
+
+    @Override
+    public synchronized boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * @return the broker
+     */
+    public BrokerImpl getBroker() {
+        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;
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ConsumerContextImpl.xtend
deleted file mode 100644 (file)
index 813f52b..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.dom.broker
-
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession
-import org.opendaylight.controller.sal.core.api.BrokerService
-import org.opendaylight.controller.sal.core.api.Consumer
-import org.osgi.framework.BundleContext
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.controller.sal.dom.broker.osgi.AbstractBrokerServiceProxy
-import com.google.common.collect.ClassToInstanceMap
-import com.google.common.collect.MutableClassToInstanceMap
-import org.opendaylight.controller.sal.dom.broker.osgi.ProxyFactory
-
-class ConsumerContextImpl implements ConsumerSession {
-
-    @Property
-    private val Consumer consumer;
-
-    @Property
-    private var BrokerImpl broker;
-
-    private val ClassToInstanceMap<BrokerService> instantiatedServices = MutableClassToInstanceMap.create();
-    private boolean closed = false;
-
-    private BundleContext context;
-
-    public new(Consumer consumer, BundleContext ctx) {
-        this._consumer = consumer;
-        this.context = ctx;
-    }
-
-    override rpc(QName rpc, CompositeNode input) {
-        return broker.invokeRpcAsync(rpc, input);
-    }
-
-    override <T extends BrokerService> T getService(Class<T> service) {
-        val localProxy = instantiatedServices.getInstance(service);
-        if(localProxy != null) {
-            return localProxy;
-        }
-        val serviceRef = context.getServiceReference(service);
-        if(serviceRef == null) {
-            return null;
-        }
-        val serviceImpl = context.getService(serviceRef);
-        
-        
-        val ret = ProxyFactory.createProxy(serviceRef,serviceImpl);
-        if(ret != null) {
-            instantiatedServices.putInstance(service, ret);
-        }
-        return ret;
-    }
-
-    override close() {
-        val toStop = instantiatedServices.values();
-        this.closed = true;
-        for (BrokerService brokerService : toStop) {
-            if(brokerService instanceof AbstractBrokerServiceProxy<?>) {
-                (brokerService as AutoCloseable).close();
-            } 
-        }
-        broker.consumerSessionClosed(this);
-    }
-
-    override isClosed() {
-        return closed;
-    }
-}
index dab8fd5cd10bb71f1d9f9df480b3ec509276cd62..430963a884b4fbaa71a7c3c12d5df9356058be0c 100644 (file)
@@ -10,7 +10,10 @@ package org.opendaylight.controller.sal.dom.broker;
 import static com.google.common.base.Preconditions.checkState;
 
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.List;
 
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
@@ -33,54 +36,34 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 
-public class GlobalBundleScanningSchemaServiceImpl implements //
-        SchemaContextProvider, //
-        SchemaService, //
-        ServiceTrackerCustomizer<SchemaServiceListener, SchemaServiceListener>, //
-        AutoCloseable {
-    private static final Logger logger = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
-
-    private ListenerRegistry<SchemaServiceListener> listeners;
-
-    private BundleContext context;
-    private final BundleScanner scanner = new BundleScanner();
-
-    private BundleTracker<ImmutableSet<Registration<URL>>> bundleTracker;
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaServiceListener, SchemaServiceListener>, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
 
+    private final ListenerRegistry<SchemaServiceListener> listeners = new ListenerRegistry<>();
     private final URLSchemaContextResolver contextResolver = new URLSchemaContextResolver();
+    private final BundleScanner scanner = new BundleScanner();
+    private final BundleContext context;
 
     private ServiceTracker<SchemaServiceListener, SchemaServiceListener> listenerTracker;
-
+    private BundleTracker<Iterable<Registration<URL>>> bundleTracker;
     private boolean starting = true;
 
-    public ListenerRegistry<SchemaServiceListener> getListeners() {
-        return listeners;
-    }
-
-    public void setListeners(final ListenerRegistry<SchemaServiceListener> listeners) {
-        this.listeners = listeners;
+    public GlobalBundleScanningSchemaServiceImpl(final BundleContext context) {
+        this.context = Preconditions.checkNotNull(context);
     }
 
     public BundleContext getContext() {
         return context;
     }
 
-    public void setContext(final BundleContext context) {
-        this.context = context;
-    }
-
     public void start() {
         checkState(context != null);
-        if (listeners == null) {
-            listeners = new ListenerRegistry<>();
-        }
 
         listenerTracker = new ServiceTracker<>(context, SchemaServiceListener.class, GlobalBundleScanningSchemaServiceImpl.this);
-        bundleTracker = new BundleTracker<ImmutableSet<Registration<URL>>>(context, BundleEvent.RESOLVED
-                        | BundleEvent.UNRESOLVED, scanner);
+        bundleTracker = new BundleTracker<>(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, scanner);
         bundleTracker.open();
         listenerTracker.open();
         starting = false;
@@ -139,7 +122,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
             try {
                 listener.getInstance().onGlobalContextUpdated(snapshot);
             } catch (Exception e) {
-                logger.error("Exception occured during invoking listener", e);
+                LOG.error("Exception occured during invoking listener", e);
             }
         }
         if (services != null) {
@@ -148,37 +131,47 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
                 try {
                     listener.onGlobalContextUpdated(snapshot);
                 } catch (Exception e) {
-                    logger.error("Exception occured during invoking listener", e);
+                    LOG.error("Exception occured during invoking listener {}", listener, e);
                 }
             }
         }
     }
 
-    private class BundleScanner implements BundleTrackerCustomizer<ImmutableSet<Registration<URL>>> {
+    private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration<URL>>> {
         @Override
-        public ImmutableSet<Registration<URL>> addingBundle(final Bundle bundle, final BundleEvent event) {
+        public Iterable<Registration<URL>> addingBundle(final Bundle bundle, final BundleEvent event) {
 
             if (bundle.getBundleId() == 0) {
-                return ImmutableSet.of();
+                return Collections.emptyList();
             }
 
-            Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
-            Builder<Registration<URL>> builder = ImmutableSet.<Registration<URL>> builder();
-            while (enumeration != null && enumeration.hasMoreElements()) {
-                Registration<URL> reg = contextResolver.registerSource(enumeration.nextElement());
-                builder.add(reg);
+            final Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
+            if (enumeration == null) {
+                return Collections.emptyList();
             }
-            ImmutableSet<Registration<URL>> urls = builder.build();
-            if(urls.isEmpty()) {
-                return urls;
+
+            final List<Registration<URL>> urls = new ArrayList<>();
+            while (enumeration.hasMoreElements()) {
+                final URL u = enumeration.nextElement();
+                try {
+                    urls.add(contextResolver.registerSource(u));
+                    LOG.debug("Registered {}", u);
+                } catch (Exception e) {
+                    LOG.warn("Failed to register {}, ignoring it", e);
+                }
             }
-            tryToUpdateSchemaContext();
-            return urls;
+
+            if (!urls.isEmpty()) {
+                LOG.debug("Loaded {} new URLs, rebuilding schema context", urls.size());
+                tryToUpdateSchemaContext();
+            }
+
+            return ImmutableList.copyOf(urls);
         }
 
         @Override
-        public void modifiedBundle(final Bundle bundle, final BundleEvent event, final ImmutableSet<Registration<URL>> object) {
-            logger.debug("Modified bundle {} {} {}", bundle, event, object);
+        public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration<URL>> object) {
+            LOG.debug("Modified bundle {} {} {}", bundle, event, object);
         }
 
         /**
@@ -188,12 +181,12 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
          */
 
         @Override
-        public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final ImmutableSet<Registration<URL>> urls) {
+        public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration<URL>> urls) {
             for (Registration<URL> url : urls) {
                 try {
                     url.close();
                 } catch (Exception e) {
-                    e.printStackTrace();
+                    LOG.warn("Failed do unregister URL {}, proceeding", url, e);
                 }
             }
             tryToUpdateSchemaContext();
@@ -212,7 +205,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements //
     }
 
     public synchronized void tryToUpdateSchemaContext() {
-        if(starting ) {
+        if (starting) {
             return;
         }
         Optional<SchemaContext> schema = contextResolver.tryToUpdateSchemaContext();
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.java
new file mode 100644 (file)
index 0000000..55a6ee7
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * 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.dom.broker;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class MountPointManagerImpl implements MountProvisionService {
+
+    private final ListenerRegistry<MountProvisionListener> listeners =
+            ListenerRegistry.create();
+    private final ConcurrentMap<InstanceIdentifier, MountPointImpl> mounts =
+            new ConcurrentHashMap<>();
+    private DataProviderService dataBroker = null;
+
+    @Override
+    public MountProvisionInstance createMountPoint(final InstanceIdentifier path) {
+        checkState(!mounts.containsKey(path), "Mount already created");
+        final MountPointImpl mount = new MountPointImpl(path);
+        registerMountPoint(mount);
+        mounts.put(path, mount);
+        notifyMountCreated(path);
+        return mount;
+    }
+
+    public void notifyMountCreated(final InstanceIdentifier identifier) {
+        for (final ListenerRegistration<MountProvisionListener> listener : listeners
+                .getListeners()) {
+            listener.getInstance().onMountPointCreated(identifier);
+        }
+    }
+
+    public Object registerMountPoint(final MountPointImpl impl) {
+        // FIXME: Why is thie commented out? Either we need it or we don't
+        // dataBroker?.registerConfigurationReader(impl.mountPath,impl.readWrapper);
+        // dataBroker?.registerOperationalReader(impl.mountPath,impl.readWrapper);
+        return null;
+    }
+
+    @Override
+    public MountProvisionInstance createOrGetMountPoint(
+            final InstanceIdentifier path) {
+        final MountPointImpl mount = mounts.get(path);
+        if (mount == null) {
+            return createMountPoint(path);
+        }
+        return mount;
+    }
+
+    @Override
+    public MountProvisionInstance getMountPoint(final InstanceIdentifier path) {
+        return mounts.get(path);
+    }
+
+    /**
+     * @return the dataBroker
+     */
+    public DataProviderService getDataBroker() {
+        return dataBroker;
+    }
+
+    /**
+     * @param dataBroker
+     *            the dataBroker to set
+     */
+    public void setDataBroker(final DataProviderService dataBroker) {
+        this.dataBroker = dataBroker;
+    }
+
+    @Override
+    public ListenerRegistration<MountProvisionListener> registerProvisionListener(
+            final MountProvisionListener listener) {
+        return listeners.register(listener);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend
deleted file mode 100644 (file)
index 023f906..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.dom.broker
-
-
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import java.util.concurrent.ConcurrentMap
-import java.util.concurrent.ConcurrentHashMap
-import static com.google.common.base.Preconditions.*;
-import org.opendaylight.controller.sal.core.api.data.DataProviderService
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener
-import org.opendaylight.yangtools.concepts.util.ListenerRegistry
-
-class MountPointManagerImpl implements MountProvisionService {
-    
-    @Property
-    DataProviderService dataBroker;
-    
-    val ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create()
-    
-    ConcurrentMap<InstanceIdentifier,MountPointImpl> mounts = new ConcurrentHashMap();
-    
-    override createMountPoint(InstanceIdentifier path) {
-        checkState(!mounts.containsKey(path),"Mount already created");
-        val mount = new MountPointImpl(path);
-        registerMountPoint(mount);
-        mounts.put(path,mount);
-        notifyMountCreated(path);
-        return mount;
-    }
-    
-    def notifyMountCreated(InstanceIdentifier identifier) {
-        for(listener : listeners) {
-            listener.instance.onMountPointCreated(identifier);
-        }
-    }
-    
-    def registerMountPoint(MountPointImpl impl) {
-        //dataBroker?.registerConfigurationReader(impl.mountPath,impl.readWrapper);
-        //dataBroker?.registerOperationalReader(impl.mountPath,impl.readWrapper);
-    }
-    
-    override registerProvisionListener(MountProvisionListener listener) {
-        listeners.register(listener)
-    }
-    
-    
-    override createOrGetMountPoint(InstanceIdentifier path) {
-        val mount = mounts.get(path);
-        if(mount === null) {
-            return createMountPoint(path)
-        }
-        return mount;
-    }
-    
-    
-    override getMountPoint(InstanceIdentifier path) {
-        mounts.get(path);
-    }
-}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.java
new file mode 100644 (file)
index 0000000..5e8c0e8
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.dom.broker;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+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);
+        this.provider = provider;
+    }
+
+    @Override
+    public RpcRegistrationWrapper addRpcImplementation(final QName rpcType,
+            final RpcImplementation implementation) throws IllegalArgumentException {
+        final RpcRegistration origReg = getBroker().getRouter()
+                .addRpcImplementation(rpcType, implementation);
+        final RpcRegistrationWrapper newReg = new RpcRegistrationWrapper(
+                origReg);
+        registrations.add(newReg);
+        return newReg;
+    }
+
+    protected boolean removeRpcImplementation(final RpcRegistrationWrapper implToRemove) {
+        return registrations.remove(implToRemove);
+    }
+
+    @Override
+    public void close() {
+        for (final RpcRegistrationWrapper reg : registrations) {
+            reg.close();
+        }
+    }
+
+    @Override
+    public RoutedRpcRegistration addMountedRpcImplementation(
+            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");
+    }
+
+    @Override
+    public Set<QName> getSupportedRpcs() {
+        return getBroker().getRouter().getSupportedRpcs();
+    }
+
+    @Override
+    public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(
+            final RpcRegistrationListener listener) {
+        return getBroker().getRouter().addRpcRegistrationListener(listener);
+    }
+
+    /**
+     * @return the provider
+     */
+    public Provider getProvider() {
+        return provider;
+    }
+
+    /**
+     * @param provider
+     *            the provider to set
+     */
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/ProviderContextImpl.xtend
deleted file mode 100644 (file)
index e641ed1..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.dom.broker
-
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession
-import org.opendaylight.controller.sal.core.api.Provider
-import org.opendaylight.controller.sal.core.api.RpcImplementation
-import org.opendaylight.yangtools.yang.common.QName
-import org.osgi.framework.BundleContext
-import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener
-import org.opendaylight.yangtools.concepts.Registration
-
-import java.util.Set
-import java.util.HashSet
-
-class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession {
-
-    @Property
-    private val Provider provider;
-
-    private val Set<Registration<?>> registrations = new HashSet();
-
-    new(Provider provider, BundleContext ctx) {
-        super(null, ctx);
-        this._provider = provider;
-    }
-
-    override addRpcImplementation(QName rpcType, RpcImplementation implementation) throws IllegalArgumentException {
-        val origReg = broker.router.addRpcImplementation(rpcType, implementation);
-        val newReg = new RpcRegistrationWrapper(origReg);
-        registrations.add(newReg);
-        return newReg;
-    }
-
-    protected def removeRpcImplementation(RpcRegistrationWrapper implToRemove) throws IllegalArgumentException {
-        registrations.remove(implToRemove);
-    }
-    
-    override close() {
-        
-        for (reg : registrations) {
-            reg.close()
-        }
-        super.close
-    }
-
-    override addMountedRpcImplementation(QName rpcType, RpcImplementation implementation) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
-    }
-
-    override getSupportedRpcs() {
-        broker.router.supportedRpcs;
-    }
-
-    override addRpcRegistrationListener(RpcRegistrationListener listener) {
-        broker.router.addRpcRegistrationListener(listener);
-    }
-}
-
-class RpcRegistrationWrapper implements RpcRegistration {
-
-
-    @Property
-    val RpcRegistration delegate
-
-    new(RpcRegistration delegate) {
-        _delegate = delegate
-    }
-
-    override getInstance() {
-        delegate.instance
-    }
-
-    override close() {
-        delegate.close
-    }
-
-    override getType() {
-        delegate.type
-    }
-}
-
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/RpcRegistrationWrapper.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/RpcRegistrationWrapper.java
new file mode 100644 (file)
index 0000000..db6c72e
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * 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.dom.broker;
+
+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 com.google.common.base.Preconditions;
+
+public class RpcRegistrationWrapper implements RpcRegistration {
+
+    private final RpcRegistration delegate;
+
+    public RpcRegistrationWrapper(final RpcRegistration delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    @Override
+    public RpcImplementation getInstance() {
+        return delegate.getInstance();
+    }
+
+    @Override
+    public void close() {
+        delegate.close();
+    }
+
+    @Override
+    public QName getType() {
+        return delegate.getType();
+    }
+
+    /**
+     * @return the delegate
+     */
+    public RpcRegistration getDelegate() {
+        return delegate;
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.java
new file mode 100644 (file)
index 0000000..53423f6
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.impl.routing.AbstractDataReadRouter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.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.CompositeNodeTOImpl;
+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
+            .getLogger(DataReaderRouter.class);
+    private final static URI NETCONF_NAMESPACE = URI
+            .create("urn:ietf:params:xml:ns:netconf:base:1.0");
+    private final static QName NETCONF_DATA = new QName(NETCONF_NAMESPACE,
+            "data");
+
+    @Override
+    protected CompositeNodeTOImpl merge(final InstanceIdentifier path,
+            final Iterable<CompositeNode> data) {
+        PathArgument pathArgument = Iterables.getLast(path.getPath(), null);
+        boolean empty = true;
+        QName name = (pathArgument == null ? null : pathArgument.getNodeType());
+        final ArrayList<Node<?>> nodes = new ArrayList<Node<?>>();
+        final HashMap<QName, SimpleNode<?>> keyNodes = new HashMap<QName, SimpleNode<?>>();
+        for (final CompositeNode dataBit : data) {
+            try {
+                if (pathArgument != null && dataBit != null) {
+                    empty = false;
+                    final Map<QName, SimpleNode<?>> keyNodesLocal = getKeyNodes(
+                            pathArgument, dataBit);
+                    nodes.addAll(this.childrenWithout(dataBit,
+                            keyNodesLocal.entrySet()));
+                } else if (dataBit != null) {
+                    empty = false;
+                    nodes.addAll(dataBit.getValue());
+                }
+            } catch (IllegalStateException e) {
+                LOG.error("BUG: Readed data for path {} was invalid", path, e);
+            }
+        }
+        if (empty) {
+            return null;
+        }
+        /**
+         * Reading from Root
+         *
+         */
+        if (pathArgument == null) {
+            return new CompositeNodeTOImpl(NETCONF_DATA, null, nodes);
+        }
+        final ArrayList<Node<?>> finalNodes = new ArrayList<Node<?>>(
+                nodes.size() + keyNodes.size());
+        finalNodes.addAll(keyNodes.values());
+        finalNodes.addAll(nodes);
+        return new CompositeNodeTOImpl(name, null, finalNodes);
+    }
+
+    protected Map<QName, SimpleNode<?>> _getKeyNodes(
+            final PathArgument argument, final CompositeNode node) {
+        return Collections.emptyMap();
+    }
+
+    protected Map<QName, SimpleNode<?>> _getKeyNodes(
+            final NodeIdentifierWithPredicates argument,
+            final CompositeNode node) {
+        final HashMap<QName, SimpleNode<?>> ret = new HashMap<QName, SimpleNode<?>>();
+        for (final Entry<QName, Object> keyValue : argument.getKeyValues()
+                .entrySet()) {
+            final List<SimpleNode<?>> simpleNode = node
+                    .getSimpleNodesByName(keyValue.getKey());
+            if (simpleNode != null && !simpleNode.isEmpty()) {
+                checkState(
+                        simpleNode.size() <= 1,
+                        "Only one simple node for key $s is allowed in node $s",
+                        keyValue.getKey(), node);
+                checkState(
+                        simpleNode.get(0).getValue() == keyValue.getValue(),
+                        "Key node must equal to instance identifier value in node $s",
+                        node);
+                ret.put(keyValue.getKey(), simpleNode.get(0));
+            }
+            final List<CompositeNode> compositeNode = node
+                    .getCompositesByName(keyValue.getKey());
+            checkState(compositeNode == null || compositeNode.isEmpty(),
+                    "Key node must be Simple Node, not composite node.");
+        }
+        return ret;
+    }
+
+    public Map<QName, SimpleNode<?>> getKeyNodes(
+            final InstanceIdentifier.PathArgument argument,
+            final CompositeNode node) {
+        if (argument instanceof InstanceIdentifier.NodeIdentifierWithPredicates) {
+            return _getKeyNodes(
+                    (InstanceIdentifier.NodeIdentifierWithPredicates) argument,
+                    node);
+        } else if (argument != null) {
+            return _getKeyNodes(argument, node);
+        } else {
+            throw new IllegalArgumentException("Unhandled parameter types: "
+                    + Arrays.<Object> asList(argument, node).toString());
+        }
+    }
+
+    private Collection<? extends Node<?>> childrenWithout(
+            final CompositeNode node,
+            final Set<Entry<QName, SimpleNode<?>>> entries) {
+        if (entries.isEmpty()) {
+            return node.getValue();
+        }
+        final List<Node<?>> filteredNodes = new ArrayList<Node<?>>();
+        for (final Node<?> scannedNode : node.getValue()) {
+            if (!entries.contains(scannedNode.getNodeType())) {
+                filteredNodes.add(scannedNode);
+            }
+        }
+        return filteredNodes;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/DataReaderRouter.xtend
deleted file mode 100644 (file)
index 95d0018..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.dom.broker.impl
-
-import java.net.URI
-import java.util.ArrayList
-import java.util.Collection
-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.md.sal.common.impl.routing.AbstractDataReadRouter
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.yangtools.yang.data.api.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.CompositeNodeTOImpl
-import org.slf4j.LoggerFactory
-
-import static com.google.common.base.Preconditions.*
-
-class DataReaderRouter extends AbstractDataReadRouter<InstanceIdentifier, CompositeNode> {
-    private static val LOG = LoggerFactory.getLogger(DataReaderRouter);
-    private static val NETCONF_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0")
-    private static val NETCONF_DATA = new QName(NETCONF_NAMESPACE,"data");
-
-    override protected merge(InstanceIdentifier path, Iterable<CompositeNode> data) {
-        val pathArgument = path.path.last;
-        var empty = true;
-        var name = pathArgument?.nodeType;
-        val nodes = new ArrayList<Node<?>>();
-        val keyNodes = new HashMap<QName, SimpleNode<?>>();
-        for(dataBit : data) {
-            try {
-                if(pathArgument != null && dataBit != null) {
-                    empty = false;
-                    val keyNodesLocal = getKeyNodes(pathArgument,dataBit);
-                    nodes.addAll(dataBit.childrenWithout(keyNodesLocal.entrySet));
-                } else if (dataBit != null) {
-                    empty = false;
-                    nodes.addAll(dataBit.children)
-                }
-            }   catch (IllegalStateException e) {
-                LOG.error("BUG: Readed data for path {} was invalid",path,e);
-            }
-        }
-        if(empty) {
-            return null;
-        }
-        /**
-         * Reading from Root
-         * 
-         */
-        if(pathArgument == null) {
-            return new CompositeNodeTOImpl(NETCONF_DATA,null,nodes);
-        }
-        val finalNodes = new ArrayList<Node<?>>();
-        finalNodes.addAll(keyNodes.values);
-        finalNodes.addAll(nodes);
-        return new CompositeNodeTOImpl(name,null,finalNodes);
-    }
-    
-    
-    
-    dispatch def Map<QName, SimpleNode<?>> getKeyNodes(PathArgument argument, CompositeNode node) {
-        return Collections.emptyMap();
-    }
-    
-    dispatch def getKeyNodes(NodeIdentifierWithPredicates argument, CompositeNode node) {
-        val ret = new HashMap<QName, SimpleNode<?>>();
-        for (keyValue : argument.keyValues.entrySet) {
-            val simpleNode = node.getSimpleNodesByName(keyValue.key);
-            if(simpleNode !== null && !simpleNode.empty) {
-                checkState(simpleNode.size <= 1,"Only one simple node for key $s is allowed in node $s",keyValue.key,node);
-                checkState(simpleNode.get(0).value == keyValue.value,"Key node must equal to instance identifier value in node $s",node);
-                ret.put(keyValue.key,simpleNode.get(0));
-            }
-            val compositeNode = node.getCompositesByName(keyValue.key);
-            checkState(compositeNode === null || compositeNode.empty,"Key node must be Simple Node, not composite node.");
-        }
-        return ret;
-    }
-    
-    def Collection<? extends Node<?>> childrenWithout(CompositeNode node, Set<Entry<QName, SimpleNode<?>>> entries) {
-        if(entries.empty) {
-            return node.children;
-        }
-        val filteredNodes = new ArrayList<Node<?>>();
-        for(scannedNode : node.children) {
-            if(!entries.contains(scannedNode.nodeType)) {
-                filteredNodes.add(scannedNode);
-            }
-        }
-        return filteredNodes;
-    }
-    
-}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.java
new file mode 100644 (file)
index 0000000..50dfbe8
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.dom.broker.impl;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.data.DataStore;
+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;
+
+public final class HashMapDataStore implements DataStore, AutoCloseable {
+    private static final Logger LOG = LoggerFactory
+            .getLogger(HashMapDataStore.class);
+
+    private final Map<InstanceIdentifier, CompositeNode> configuration = new ConcurrentHashMap<InstanceIdentifier, CompositeNode>();
+    private final Map<InstanceIdentifier, CompositeNode> operational = new ConcurrentHashMap<InstanceIdentifier, CompositeNode>();
+
+    @Override
+    public boolean containsConfigurationPath(final InstanceIdentifier path) {
+        return configuration.containsKey(path);
+    }
+
+    @Override
+    public boolean containsOperationalPath(final InstanceIdentifier path) {
+        return operational.containsKey(path);
+    }
+
+    @Override
+    public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
+        return configuration.keySet();
+    }
+
+    @Override
+    public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
+        return operational.keySet();
+    }
+
+    @Override
+    public CompositeNode readConfigurationData(final InstanceIdentifier path) {
+        LOG.trace("Reading configuration path {}", path);
+        return configuration.get(path);
+    }
+
+    @Override
+    public CompositeNode readOperationalData(InstanceIdentifier path) {
+        LOG.trace("Reading operational path {}", path);
+        return operational.get(path);
+    }
+
+    @Override
+    public DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
+            final DataModification<InstanceIdentifier, CompositeNode> modification) {
+        return new HashMapDataStoreTransaction(modification, this);
+    }
+
+    public RpcResult<Void> rollback(HashMapDataStoreTransaction transaction) {
+        return Rpcs.<Void> getRpcResult(true, null,
+                Collections.<RpcError> emptySet());
+    }
+
+    public RpcResult<Void> finish(HashMapDataStoreTransaction transaction) {
+        final DataModification<InstanceIdentifier, CompositeNode> modification = transaction
+                .getModification();
+        for (final InstanceIdentifier removal : modification
+                .getRemovedConfigurationData()) {
+            LOG.trace("Removing configuration path {}", removal);
+            remove(configuration, removal);
+        }
+        for (final InstanceIdentifier removal : modification
+                .getRemovedOperationalData()) {
+            LOG.trace("Removing operational path {}", removal);
+            remove(operational, removal);
+        }
+        if (LOG.isTraceEnabled()) {
+            for (final InstanceIdentifier a : modification
+                    .getUpdatedConfigurationData().keySet()) {
+                LOG.trace("Adding configuration path {}", a);
+            }
+            for (final InstanceIdentifier a : modification
+                    .getUpdatedOperationalData().keySet()) {
+                LOG.trace("Adding operational path {}", a);
+            }
+        }
+        configuration.putAll(modification.getUpdatedConfigurationData());
+        operational.putAll(modification.getUpdatedOperationalData());
+
+        return Rpcs.<Void> getRpcResult(true, null,
+                Collections.<RpcError> emptySet());
+    }
+
+    public void remove(final Map<InstanceIdentifier, CompositeNode> map,
+            final InstanceIdentifier identifier) {
+        Set<InstanceIdentifier> affected = new HashSet<InstanceIdentifier>();
+        for (final InstanceIdentifier path : map.keySet()) {
+            if (identifier.contains(path)) {
+                affected.add(path);
+            }
+        }
+        for (final InstanceIdentifier pathToRemove : affected) {
+            LOG.trace("Removed path {}", pathToRemove);
+            map.remove(pathToRemove);
+        }
+    }
+
+    @Override
+    public void close() {
+        // NOOP
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStore.xtend
deleted file mode 100644 (file)
index 12835cc..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.dom.broker.impl
-
-import org.opendaylight.controller.md.sal.common.api.data.DataModification
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction
-import org.opendaylight.yangtools.yang.common.RpcResult
-import java.util.Map
-import java.util.concurrent.ConcurrentHashMap
-import org.opendaylight.controller.sal.common.util.Rpcs
-import java.util.Collections
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.controller.sal.core.api.data.DataStore
-import java.util.HashSet
-import org.slf4j.LoggerFactory
-import org.slf4j.Logger
-
-final class HashMapDataStore implements DataStore, AutoCloseable {
-    private val Logger LOG = LoggerFactory.getLogger(HashMapDataStore)
-
-    val Map<InstanceIdentifier, CompositeNode> configuration = new ConcurrentHashMap();
-    val Map<InstanceIdentifier, CompositeNode> operational = new ConcurrentHashMap();
-    
-    
-    
-    override containsConfigurationPath(InstanceIdentifier path) {
-        return configuration.containsKey(path)
-    }
-    
-    override containsOperationalPath(InstanceIdentifier path) {
-        return operational.containsKey(path)
-    }
-    
-    override getStoredConfigurationPaths() {
-        configuration.keySet
-    }
-    
-    override getStoredOperationalPaths() {
-        operational.keySet
-    }
-
-    override readConfigurationData(InstanceIdentifier path) {
-        LOG.trace("Reading configuration path {}", path)
-        configuration.get(path);
-    }
-
-    override readOperationalData(InstanceIdentifier path) {
-        LOG.trace("Reading operational path {}", path)
-        operational.get(path);
-    }
-
-
-
-    override requestCommit(DataModification<InstanceIdentifier, CompositeNode> modification) {
-        return new HashMapDataStoreTransaction(modification, this);
-    }
-
-    def RpcResult<Void> rollback(HashMapDataStoreTransaction transaction) {
-        return Rpcs.getRpcResult(true, null, Collections.emptySet);
-    }
-
-    def RpcResult<Void> finish(HashMapDataStoreTransaction transaction) {
-        val modification = transaction.modification;
-        for (removal : modification.removedConfigurationData) {
-            LOG.trace("Removing configuration path {}", removal)
-            remove(configuration,removal);
-        }
-        for (removal : modification.removedOperationalData) {
-            LOG.trace("Removing operational path {}", removal)
-            remove(operational,removal);
-        }
-        if (LOG.isTraceEnabled()) {
-            for (a : modification.updatedConfigurationData.keySet) {
-                LOG.trace("Adding configuration path {}", a)
-            }
-            for (a : modification.updatedOperationalData.keySet) {
-                LOG.trace("Adding operational path {}", a)
-            }
-        }
-        configuration.putAll(modification.updatedConfigurationData);
-        operational.putAll(modification.updatedOperationalData);
-
-        return Rpcs.getRpcResult(true, null, Collections.emptySet);
-    }
-    
-    def remove(Map<InstanceIdentifier, CompositeNode> map, InstanceIdentifier identifier) {
-        val affected = new HashSet<InstanceIdentifier>();
-        for(path : map.keySet) {
-            if(identifier.contains(path)) {
-                affected.add(path);
-            }
-        }
-        for(pathToRemove : affected) {
-            LOG.trace("Removed path {}", pathToRemove)
-            map.remove(pathToRemove);
-        }
-        
-    }
-
-
-    override close()  {
-        // NOOP
-    }
-    
-}
-
-class HashMapDataStoreTransaction implements // 
-DataCommitTransaction<InstanceIdentifier, CompositeNode> {
-    @Property
-    val DataModification<InstanceIdentifier, CompositeNode> modification
-
-    @Property
-    val HashMapDataStore datastore;
-
-    new(
-        DataModification<InstanceIdentifier, CompositeNode> modify,
-        HashMapDataStore store
-    ) {
-        _modification = modify;
-        _datastore = store;
-    }
-
-    override finish() throws IllegalStateException {
-        datastore.finish(this);
-
-    }
-
-    override getModification() {
-        this._modification;
-    }
-
-    override rollback() throws IllegalStateException {
-        datastore.rollback(this);
-    }
-}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStoreTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/HashMapDataStoreTransaction.java
new file mode 100644 (file)
index 0000000..bb66594
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class HashMapDataStoreTransaction implements
+        DataCommitTransaction<InstanceIdentifier, CompositeNode> {
+    private final DataModification<InstanceIdentifier, CompositeNode> modification;
+    private final HashMapDataStore datastore;
+
+    HashMapDataStoreTransaction(
+            final DataModification<InstanceIdentifier, CompositeNode> modify,
+            final HashMapDataStore store) {
+        modification = modify;
+        datastore = store;
+    }
+
+    @Override
+    public RpcResult<Void> finish() throws IllegalStateException {
+        return datastore.finish(this);
+    }
+
+    @Override
+    public DataModification<InstanceIdentifier, CompositeNode> getModification() {
+        return this.modification;
+    }
+
+    @Override
+    public RpcResult<Void> rollback() throws IllegalStateException {
+        return datastore.rollback(this);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.java
new file mode 100644 (file)
index 0000000..c2d6add
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.dom.broker.osgi;
+
+import java.util.Arrays;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.osgi.framework.ServiceReference;
+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.notify.NotificationPublishService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationService;
+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.RpcProvisionRegistry;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+
+@SuppressWarnings("unchecked")
+public class ProxyFactory {
+
+    public static <T extends BrokerService> T createProxy(
+            final ServiceReference<T> serviceRef, final T service) {
+
+        Object _createProxyImpl = ProxyFactory.createProxyImpl(serviceRef,
+                service);
+        return ((T) _createProxyImpl);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final DataBrokerService service) {
+
+        return new DataBrokerServiceProxy(
+                ((ServiceReference<DataBrokerService>) ref), service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final DataProviderService service) {
+
+        return new DataProviderServiceProxy(
+                ((ServiceReference<DataProviderService>) ref), service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final NotificationPublishService service) {
+
+        return new NotificationPublishServiceProxy(
+                ((ServiceReference<NotificationPublishService>) ref), service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final NotificationService service) {
+
+        return new NotificationServiceProxy(
+                ((ServiceReference<NotificationService>) ref), service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final MountProvisionService service) {
+
+        return new MountProviderServiceProxy(
+                ((ServiceReference<MountProvisionService>) ref), service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final SchemaService service) {
+
+        return new SchemaServiceProxy(((ServiceReference<SchemaService>) ref),
+                service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> ref,
+            final RpcProvisionRegistry service) {
+
+        return new RpcProvisionRegistryProxy(
+                ((ServiceReference<RpcProvisionRegistry>) ref), service);
+    }
+
+    private static DOMDataBrokerProxy _createProxyImpl(
+            final ServiceReference<?> ref, final DOMDataBroker service) {
+
+        return new DOMDataBrokerProxy(((ServiceReference<DOMDataBroker>) ref),
+                service);
+    }
+
+    private static Object _createProxyImpl(final ServiceReference<?> reference,
+            final BrokerService service) {
+
+        throw new IllegalArgumentException("Not supported class: "
+                + service.getClass().getName());
+    }
+
+    private static Object createProxyImpl(final ServiceReference<?> ref,
+            final BrokerService service) {
+
+        if (service instanceof DOMDataBroker) {
+            return _createProxyImpl(ref, (DOMDataBroker) service);
+        } else if (service instanceof RpcProvisionRegistry) {
+            return _createProxyImpl(ref, (RpcProvisionRegistry) service);
+        } else if (service instanceof DataProviderService) {
+            return _createProxyImpl(ref, (DataProviderService) service);
+        } else if (service instanceof MountProvisionService) {
+            return _createProxyImpl(ref, (MountProvisionService) service);
+        } else if (service instanceof NotificationPublishService) {
+            return _createProxyImpl(ref, (NotificationPublishService) service);
+        } else if (service instanceof DataBrokerService) {
+            return _createProxyImpl(ref, (DataBrokerService) service);
+        } else if (service instanceof SchemaService) {
+            return _createProxyImpl(ref, (SchemaService) service);
+        } else if (service instanceof NotificationService) {
+            return _createProxyImpl(ref, (NotificationService) service);
+        } else if (service != null) {
+            return _createProxyImpl(ref, service);
+        } else {
+            throw new IllegalArgumentException("Unhandled parameter types: "
+                    + Arrays.<Object> asList(ref, service).toString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/ProxyFactory.xtend
deleted file mode 100644 (file)
index d0afc3f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.dom.broker.osgi
-
-import org.opendaylight.controller.sal.core.api.BrokerService
-import org.osgi.framework.ServiceReference
-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.notify.NotificationPublishService
-import org.opendaylight.controller.sal.core.api.notify.NotificationService
-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.RpcProvisionRegistry
-import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker
-
-class ProxyFactory {
-
-    static def <T extends BrokerService> T createProxy(ServiceReference<T> serviceRef, T service) {
-        return createProxyImpl(serviceRef, service) as T;
-    }
-
-
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, DataBrokerService service) {
-        new DataBrokerServiceProxy(ref as ServiceReference<DataBrokerService>, service);
-    }
-
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, DataProviderService service) {
-        new DataProviderServiceProxy(ref as ServiceReference<DataProviderService>, service);
-    }
-    
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, NotificationPublishService service) {
-        new NotificationPublishServiceProxy(ref as ServiceReference<NotificationPublishService>, service);
-    }
-    
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, NotificationService service) {
-        new NotificationServiceProxy(ref as ServiceReference<NotificationService>, service);
-    }
-
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, MountProvisionService service) {
-        new MountProviderServiceProxy(ref as ServiceReference<MountProvisionService>, service);
-    }
-
-
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, SchemaService service) {
-        new SchemaServiceProxy(ref as ServiceReference<SchemaService>, service);
-    }
-
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, RpcProvisionRegistry service) {
-        new RpcProvisionRegistryProxy(ref as ServiceReference<RpcProvisionRegistry>, service);
-    }
-    
-    private static def dispatch createProxyImpl(ServiceReference<?> ref, DOMDataBroker service) {
-        new DOMDataBrokerProxy(ref as ServiceReference<DOMDataBroker>, service)
-    }
-    
-
-    private static def dispatch createProxyImpl(ServiceReference<?> reference, BrokerService service) {
-        throw new IllegalArgumentException("Not supported class");
-    }
-
-}
index 344b3f32763c2fcb9c45047ae7c4e34f0b6e087d..f893f96d18c59e94df9fe7961b8856b214a6f196 100644 (file)
@@ -17,20 +17,19 @@ import org.osgi.framework.ServiceRegistration;
 
 public class SchemaServiceActivator implements BundleActivator {
 
-    
+
     private ServiceRegistration<SchemaService> schemaServiceReg;
     private GlobalBundleScanningSchemaServiceImpl schemaService;
 
     @Override
-    public void start(BundleContext context) throws Exception {
-        schemaService = new GlobalBundleScanningSchemaServiceImpl();
-        schemaService.setContext(context);
+    public void start(final BundleContext context) {
+        schemaService = new GlobalBundleScanningSchemaServiceImpl(context);
         schemaService.start();
         schemaServiceReg = context.registerService(SchemaService.class, schemaService, new Hashtable<String,String>());
     }
-    
+
     @Override
-    public void stop(BundleContext context) throws Exception {
+    public void stop(final BundleContext context) throws Exception {
         schemaServiceReg.unregister();
         schemaService.close();
     }
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangDataOperations.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangDataOperations.java
new file mode 100644 (file)
index 0000000..0f8ce1d
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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.dom.broker.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+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;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+
+public class YangDataOperations {
+
+    public static CompositeNode merge(final DataSchemaNode schema,
+            final CompositeNode stored, final CompositeNode modified,
+            final boolean config) {
+        if (stored == null) {
+            return modified;
+        }
+
+        Preconditions.checkArgument(schema instanceof ListSchemaNode
+                || schema instanceof ContainerSchemaNode,
+                "Supplied node is not data node container.");
+
+        return YangDataOperations.mergeContainer((DataNodeContainer) schema,
+                stored, modified, config);
+    }
+
+    private static Iterable<? extends Node<?>> _mergeMultiple(
+            final LeafSchemaNode node, final List<Node<?>> original,
+            final List<Node<?>> modified, final boolean configurational) {
+        checkArgument(original.size() == 1);
+        checkArgument(modified.size() == 1);
+
+        return modified;
+    }
+
+    private static Iterable<? extends Node<?>> _mergeMultiple(
+            final LeafListSchemaNode node, final List<Node<?>> original,
+            final List<Node<?>> modified, final boolean configurational) {
+        return modified;
+    }
+
+    private static Iterable<? extends Node<?>> _mergeMultiple(
+            final ContainerSchemaNode node, final List<Node<?>> original,
+            final List<Node<?>> modified, final boolean configurational) {
+        checkArgument(original.size() == 1);
+        checkArgument(modified.size() == 1);
+        return Collections.singletonList(merge(node,
+                (CompositeNode) original.get(0),
+                (CompositeNode) modified.get(0), configurational));
+    }
+
+    private static Iterable<? extends Node<?>> _mergeMultiple(
+            final ListSchemaNode node, final List<Node<?>> original,
+            final List<Node<?>> modified, final boolean configurational) {
+
+        if (node.getKeyDefinition() == null
+                || node.getKeyDefinition().isEmpty()) {
+            return modified;
+        }
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final Map<Map<QName, Object>, CompositeNode> originalMap = YangDataUtils
+                .toIndexMap((List) original, node.getKeyDefinition());
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final Map<Map<QName, Object>, CompositeNode> modifiedMap = YangDataUtils
+                .toIndexMap((List) modified, node.getKeyDefinition());
+
+        final List<Node<?>> mergedNodes = new ArrayList<Node<?>>(
+                original.size() + modified.size());
+        for (final Map.Entry<Map<QName, Object>, CompositeNode> entry : modifiedMap
+                .entrySet()) {
+            final CompositeNode originalEntry = originalMap.get(entry.getKey());
+            if (originalEntry != null) {
+                originalMap.remove(entry.getKey());
+                mergedNodes.add(merge(node, originalEntry, entry.getValue(),
+                        configurational));
+            } else {
+                mergedNodes.add(entry.getValue());
+            }
+        }
+        mergedNodes.addAll(originalMap.values());
+        return mergedNodes;
+    }
+
+    private static Iterable<? extends Node<?>> mergeMultiple(
+            final DataSchemaNode node, final List<Node<?>> original,
+            final List<Node<?>> modified, final boolean configurational) {
+        if (node instanceof ContainerSchemaNode) {
+            return _mergeMultiple((ContainerSchemaNode) node, original,
+                    modified, configurational);
+        } else if (node instanceof LeafListSchemaNode) {
+            return _mergeMultiple((LeafListSchemaNode) node, original,
+                    modified, configurational);
+        } else if (node instanceof LeafSchemaNode) {
+            return _mergeMultiple((LeafSchemaNode) node, original, modified,
+                    configurational);
+        } else if (node instanceof ListSchemaNode) {
+            return _mergeMultiple((ListSchemaNode) node, original, modified,
+                    configurational);
+        } else {
+            throw new IllegalArgumentException("Unhandled parameter types: "
+                    + Arrays.<Object> asList(node, original, modified,
+                            configurational).toString());
+        }
+    }
+
+    private static CompositeNode mergeContainer(final DataNodeContainer schema,
+            final CompositeNode stored, final CompositeNode modified,
+            final boolean config) {
+        if (stored == null) {
+            return modified;
+        }
+        Preconditions.checkNotNull(stored);
+        Preconditions.checkNotNull(modified);
+        Preconditions.checkArgument(Objects.equals(stored.getNodeType(),
+                modified.getNodeType()));
+
+        final List<Node<?>> mergedChildNodes = new ArrayList<Node<?>>(stored
+                .getChildren().size() + modified.getChildren().size());
+        final Set<QName> toProcess = new HashSet<QName>(stored.keySet());
+        toProcess.addAll(modified.keySet());
+
+        for (QName qname : toProcess) {
+            final DataSchemaNode schemaChild = schema.getDataChildByName(qname);
+            final List<Node<?>> storedChildren = stored.get(qname);
+            final List<Node<?>> modifiedChildren = modified.get(qname);
+
+            if (modifiedChildren != null && !modifiedChildren.isEmpty()) {
+                if (storedChildren == null || storedChildren.isEmpty()
+                        || schemaChild == null) {
+                    mergedChildNodes.addAll(modifiedChildren);
+                } else {
+                    final Iterable<? extends Node<?>> _mergeMultiple = mergeMultiple(
+                            schemaChild, storedChildren, modifiedChildren,
+                            config);
+                    Iterables.addAll(mergedChildNodes, _mergeMultiple);
+                }
+            } else if (storedChildren != null && !storedChildren.isEmpty()) {
+                mergedChildNodes.addAll(storedChildren);
+            }
+        }
+        return new CompositeNodeTOImpl(stored.getNodeType(), null,
+                mergedChildNodes);
+    }
+}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangDataOperations.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/util/YangDataOperations.xtend
deleted file mode 100644 (file)
index d80e405..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.dom.broker.util
-
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
-import static com.google.common.base.Preconditions.*;
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-import java.util.ArrayList
-
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
-import java.util.List
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
-import java.util.Collections
-import java.util.HashSet
-import org.opendaylight.yangtools.yang.common.QName
-import static extension org.opendaylight.controller.sal.dom.broker.util.YangDataUtils.*;
-
-class YangDataOperations {
-
-    static def CompositeNode merge(DataSchemaNode schema, CompositeNode stored, CompositeNode modified, boolean config) {
-        if (stored === null) {
-            return modified;
-        }
-
-        if (schema instanceof ListSchemaNode || schema instanceof ContainerSchemaNode) {
-            return mergeContainer(schema as DataNodeContainer, stored, modified, config);
-        }
-        throw new IllegalArgumentException("Supplied node is not data node container.");
-    }
-
-    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(LeafSchemaNode node, List<Node<?>> original,
-        List<Node<?>> modified, boolean configurational) {
-        checkArgument(original.size === 1);
-        checkArgument(modified.size === 1);
-        
-        return modified;
-    }
-
-    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(LeafListSchemaNode node,
-        List<Node<?>> original, List<Node<?>> modified, boolean configurational) {
-        return modified;
-    }
-
-    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(ContainerSchemaNode node,
-        List<Node<?>> original, List<Node<?>> modified, boolean configurational) {
-        checkArgument(original.size === 1);
-        checkArgument(modified.size === 1);
-        return Collections.singletonList(
-            merge(node, original.get(0) as CompositeNode, modified.get(0) as CompositeNode, configurational));
-    }
-
-    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(ListSchemaNode node, List<Node<?>> original,
-        List<Node<?>> modified, boolean configurational) {
-        
-        if(node.keyDefinition === null || node.keyDefinition.empty) {
-            return modified;
-        }
-        val originalMap = (original as List).toIndexMap(node.keyDefinition);
-        val modifiedMap = (modified as List).toIndexMap(node.keyDefinition);
-        
-        val List<Node<?>> mergedNodes = new ArrayList(original.size + modified.size);
-        for(entry : modifiedMap.entrySet) {
-            val originalEntry = originalMap.get(entry.key);
-            if(originalEntry != null) {
-                originalMap.remove(entry.key);
-                mergedNodes.add(merge(node,originalEntry,entry.value,configurational));
-            } else {
-                mergedNodes.add(entry.value);
-            }
-        }
-        mergedNodes.addAll(originalMap.values);
-        return mergedNodes;
-    }
-
-    static private def CompositeNode mergeContainer(DataNodeContainer schema, CompositeNode stored,
-        CompositeNode modified, boolean config) {
-        if (stored == null) {
-            return modified;
-        }
-        checkNotNull(stored)
-        checkNotNull(modified)
-        checkArgument(stored.nodeType == modified.nodeType);
-
-        val mergedChildNodes = new ArrayList<Node<?>>(stored.children.size + modified.children.size);
-        
-        val toProcess = new HashSet<QName>(stored.keySet);
-        toProcess.addAll(modified.keySet);
-        
-        for (qname : toProcess) {
-            val schemaChild = schema.getDataChildByName(qname);
-            val storedChildren = stored.get(qname);
-            val modifiedChildren = modified.get(qname);
-
-            if (modifiedChildren !== null && !modifiedChildren.empty) {
-                if (storedChildren === null || storedChildren.empty || schemaChild === null) {
-                    mergedChildNodes.addAll(modifiedChildren);
-                } else {
-                    mergedChildNodes.addAll(mergeMultiple(schemaChild, storedChildren, modifiedChildren, config));
-                }
-            } else if (storedChildren !== null && !storedChildren.empty) {
-                mergedChildNodes.addAll(storedChildren);
-            }
-        }
-        return new CompositeNodeTOImpl(stored.nodeType, null, mergedChildNodes);
-    }
-
-}
diff --git a/opendaylight/md-sal/sal-dom-it/pom.xml b/opendaylight/md-sal/sal-dom-it/pom.xml
deleted file mode 100644 (file)
index 5604a87..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>sal-parent</artifactId>
-        <version>1.0-SNAPSHOT</version>
-    </parent>
-    <artifactId>sal-dom-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>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>sal-broker-impl</artifactId>
-            <version>1.0-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>yang-data-util</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-simple</artifactId>
-            <version>1.7.2</version>
-            <scope>runtime</scope>
-        </dependency>
-    </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-assembly-plugin</artifactId>
-                <version>2.4</version>
-                <configuration>
-                    <descriptorRefs>
-                        <descriptorRef>jar-with-dependencies</descriptorRef>
-                    </descriptorRefs>
-                    <archive>
-                        <manifest>
-                            <mainClass>org.opendaylight.controller.sal.demo.SALDemo</mainClass>
-                        </manifest>
-                    </archive>
-                </configuration>
-                <executions>
-                    <execution>
-                        <id>make-assembly</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>single</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-</project>
index e4c7c0c64799cb31adb93425301b4ba94a44806e..c2d245badbca059dfbff3d7bdbf2b50ba30efa1d 100644 (file)
       <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>
index d0eaa36dde23bd118a47a38b519139ccaed31a7a..af763cce0d20e0b03a76a1321580dcd71571446c 100644 (file)
@@ -7,18 +7,67 @@
  */
 package org.opendaylight.controller.sal.rest.api;
 
+import org.opendaylight.yangtools.yang.common.QName;
+
 public class Draft02 {
-    public static class MediaTypes {
-        public static final String API = "application/yang.api";
-        public static final String DATASTORE = "application/yang.datastore";
-        public static final String DATA = "application/yang.data";
-        public static final String OPERATION = "application/yang.operation";
-        public static final String PATCH = "application/yang.patch";
-        public static final String PATCH_STATUS = "application/yang.patch-status";
-        public static final String STREAM = "application/yang.stream";
+    public static interface MediaTypes {
+        String API = "application/yang.api";
+        String DATASTORE = "application/yang.datastore";
+        String DATA = "application/yang.data";
+        String OPERATION = "application/yang.operation";
+        String PATCH = "application/yang.patch";
+        String PATCH_STATUS = "application/yang.patch-status";
+        String STREAM = "application/yang.stream";
+    }
+
+    public static interface RestConfModule {
+        String REVISION = "2013-10-19";
+
+        String NAME = "ietf-restconf";
+
+        String NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf";
+
+        String RESTCONF_GROUPING_SCHEMA_NODE = "restconf";
+
+        String RESTCONF_CONTAINER_SCHEMA_NODE = "restconf";
+
+        String MODULES_CONTAINER_SCHEMA_NODE = "modules";
+
+        String MODULE_LIST_SCHEMA_NODE = "module";
+
+        String STREAMS_CONTAINER_SCHEMA_NODE = "streams";
+
+        String STREAM_LIST_SCHEMA_NODE = "stream";
+
+        String OPERATIONS_CONTAINER_SCHEMA_NODE = "operations";
+
+        String ERRORS_GROUPING_SCHEMA_NODE = "errors";
+
+        String ERRORS_CONTAINER_SCHEMA_NODE = "errors";
+
+        String ERROR_LIST_SCHEMA_NODE = "error";
+
+        QName IETF_RESTCONF_QNAME = QName.create( Draft02.RestConfModule.NAMESPACE,
+                                                  Draft02.RestConfModule.REVISION,
+                                                  Draft02.RestConfModule.NAME );
+
+        QName ERRORS_CONTAINER_QNAME = QName.create( IETF_RESTCONF_QNAME, ERRORS_CONTAINER_SCHEMA_NODE );
+
+        QName ERROR_LIST_QNAME = QName.create( IETF_RESTCONF_QNAME, ERROR_LIST_SCHEMA_NODE );
+
+        QName ERROR_TYPE_QNAME = QName.create( IETF_RESTCONF_QNAME, "error-type" );
+
+        QName ERROR_TAG_QNAME = QName.create( IETF_RESTCONF_QNAME, "error-tag" );
+
+        QName ERROR_APP_TAG_QNAME = QName.create( IETF_RESTCONF_QNAME, "error-app-tag" );
+
+        QName ERROR_MESSAGE_QNAME = QName.create( IETF_RESTCONF_QNAME, "error-message" );
+
+        QName ERROR_INFO_QNAME = QName.create( IETF_RESTCONF_QNAME, "error-info" );
     }
-    
-    public static class Paths {
-        
+
+
+    public static interface Paths {
+
     }
 }
index ea0f149d296887f4c713ba32e675ee8e4b8fbf54..1e5bfbd6b93e7fe540931a0f6f828625a4a963c1 100644 (file)
@@ -11,6 +11,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.io.IOException;
 import java.net.URI;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -84,17 +85,22 @@ class JsonMapper {
     private void writeChildrenOfParent(final JsonWriter writer, final CompositeNode parent, final DataNodeContainer parentSchema)
             throws IOException {
         checkNotNull(parent);
-        checkNotNull(parentSchema);
+
+        Set<DataSchemaNode> parentSchemaChildNodes = parentSchema == null ?
+                                   Collections.<DataSchemaNode>emptySet() : parentSchema.getChildNodes();
+
 
         for (Node<?> child : parent.getValue()) {
-            DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
+            DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchemaChildNodes);
 
             if (childSchema == null) {
-                throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName()
-                        + "\" is not conform to schema");
-            }
+                // Node may not conform to schema or allows "anyxml" - we'll process it.
 
-            if (childSchema instanceof ContainerSchemaNode) {
+                logger.debug( "No schema found for data node \"" + child.getNodeType() );
+
+                handleNoSchemaFound( writer, child, parent );
+            }
+            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);
@@ -123,7 +129,7 @@ class JsonMapper {
         }
 
         for (Node<?> child : parent.getValue()) {
-            DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
+            DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchemaChildNodes);
             if (childSchema instanceof LeafListSchemaNode) {
                 foundLeafLists.remove(childSchema);
             } else if (childSchema instanceof ListSchemaNode) {
@@ -132,6 +138,22 @@ class JsonMapper {
         }
     }
 
+    private void handleNoSchemaFound( final JsonWriter writer, final Node<?> node,
+                                      final CompositeNode parent ) throws IOException {
+        if( node instanceof SimpleNode<?> ) {
+            writeName( node, null, writer );
+            Object value = node.getValue();
+            if( value != null ) {
+                writer.value( String.valueOf( value ) );
+            }
+        } else { // CompositeNode
+            Preconditions.checkState( node instanceof CompositeNode,
+                    "Data representation of Container should be CompositeNode - " + node.getNodeType() );
+
+            writeContainer( writer, (CompositeNode) node, null );
+        }
+    }
+
     private DataSchemaNode findFirstSchemaForNode(final Node<?> node, final Set<DataSchemaNode> dataSchemaNode) {
         for (DataSchemaNode dsn : dataSchemaNode) {
             if (node.getNodeType().equals(dsn.getQName())) {
@@ -301,7 +323,7 @@ class JsonMapper {
 
     private void writeName(final Node<?> node, final DataSchemaNode schema, final JsonWriter writer) throws IOException {
         String nameForOutput = node.getNodeType().getLocalName();
-        if (schema.isAugmenting()) {
+        if ( schema != null && schema.isAugmenting()) {
             ControllerContext contContext = ControllerContext.getInstance();
             CharSequence moduleName = null;
             if (mountPoint == null) {
index 0d73485c80b3fcbceaddf7235d93737cad9fa40c..856e09fabd21a1083cbe716890ba22ae3b2d5bc6 100644 (file)
@@ -16,13 +16,17 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.Provider;
 
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Provider
 @Consumes({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON,
@@ -30,6 +34,8 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 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) {
         return true;
@@ -42,9 +48,11 @@ public enum JsonToCompositeNodeProvider implements MessageBodyReader<CompositeNo
         JsonReader jsonReader = new JsonReader();
         try {
             return jsonReader.read(entityStream);
-        } catch (UnsupportedFormatException e) {
-            throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage())
-                    .build());
+        } catch (Exception e) {
+            LOG.debug( "Error parsing json input", e );
+            throw new RestconfDocumentedException(
+                            "Error parsing input: " + e.getMessage(),
+                            ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE );
         }
     }
 
index 6166a084c19c1e9e80480c2d3745e04bc9b28eca..a5fd7bdaab2646f35e7463f9698f15ca217de11f 100644 (file)
@@ -16,8 +16,15 @@ import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
 
+import com.google.common.collect.ImmutableSet;
+
 public class RestconfApplication extends Application {
 
+    @Override
+    public Set<Class<?>> getClasses() {
+        return ImmutableSet.<Class<?>>of( RestconfDocumentedExceptionMapper.class );
+    }
+
     @Override
     public Set<Object> getSingletons() {
         Set<Object> singletons = new HashSet<>();
@@ -36,4 +43,5 @@ public class RestconfApplication extends Application {
         return singletons;
     }
 
+
 }
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfDocumentedExceptionMapper.java
new file mode 100644 (file)
index 0000000..456354b
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2014 Brocade Communications 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.rest.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map.Entry;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import javax.xml.parsers.DocumentBuilderFactory;
+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.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import static org.opendaylight.controller.sal.rest.api.Draft02.RestConfModule.*;
+
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError;
+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.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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
+ * the RESTCONF RFC draft.
+ *
+ * @author Thomas Pantelis
+ */
+@Provider
+public class RestconfDocumentedExceptionMapper implements ExceptionMapper<RestconfDocumentedException> {
+
+    private final static Logger LOG = LoggerFactory.getLogger( RestconfDocumentedExceptionMapper.class );
+
+    @Context
+    private HttpHeaders headers;
+
+    @Override
+    public Response toResponse( RestconfDocumentedException exception ) {
+
+        LOG.debug( "In toResponse: {}", exception.getMessage() );
+
+        // Default to the content type if there's no Accept header
+
+        MediaType mediaType = headers.getMediaType();
+
+        List<MediaType> accepts = headers.getAcceptableMediaTypes();
+
+        LOG.debug( "Accept headers: {}", accepts );
+
+        if( accepts != null && accepts.size() > 0 ) {
+            mediaType = accepts.get( 0 ); // just pick the first one
+        }
+
+        LOG.debug( "Using MediaType: {}",  mediaType );
+
+        List<RestconfError> errors = exception.getErrors();
+        if( errors.isEmpty() ) {
+            // We don't actually want to send any content but, if we don't set any content here,
+            // the tomcat front-end will send back an html error report. To prevent that, set a
+            // single space char in the entity.
+
+            return Response.status( exception.getStatus() )
+                                    .type( MediaType.TEXT_PLAIN_TYPE )
+                                    .entity( " " ).build();
+        }
+
+        Status status = errors.iterator().next().getErrorTag().getStatusCode();
+
+        ControllerContext context = ControllerContext.getInstance();
+        DataNodeContainer errorsSchemaNode = (DataNodeContainer)context.getRestconfModuleErrorsSchemaNode();
+
+        if( errorsSchemaNode == null ) {
+            return Response.status( status )
+                           .type( MediaType.TEXT_PLAIN_TYPE )
+                           .entity( exception.getMessage() ).build();
+        }
+
+        ImmutableList.Builder<Node<?>> errorNodes = ImmutableList.<Node<?>> builder();
+        for( RestconfError error: errors ) {
+            errorNodes.add( toDomNode( error ) );
+        }
+
+        ImmutableCompositeNode errorsNode =
+                         ImmutableCompositeNode.create( ERRORS_CONTAINER_QNAME, errorNodes.build() );
+
+        Object responseBody;
+        if( mediaType.getSubtype().endsWith( "json" ) ) {
+            responseBody = toJsonResponseBody( errorsNode, errorsSchemaNode );
+        }
+        else {
+            responseBody = toXMLResponseBody( errorsNode, errorsSchemaNode );
+        }
+
+        return Response.status( status ).type( mediaType ).entity( responseBody ).build();
+    }
+
+    private Object toJsonResponseBody( ImmutableCompositeNode errorsNode,
+                                       DataNodeContainer errorsSchemaNode ) {
+
+        JsonMapper jsonMapper = new JsonMapper();
+
+        Object responseBody = null;
+        try {
+            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+            JsonWriter writer = new JsonWriter( new OutputStreamWriter( outStream, "UTF-8" ) );
+            writer.setIndent( "    " );
+
+            jsonMapper.write( writer, errorsNode, errorsSchemaNode, null );
+            writer.flush();
+
+            responseBody = outStream.toString( "UTF-8" );
+        }
+        catch( IOException e ) {
+            LOG.error( "Error writing error response body", e );
+        }
+
+        return responseBody;
+    }
+
+    private Object toXMLResponseBody( ImmutableCompositeNode errorsNode,
+                                      DataNodeContainer errorsSchemaNode ) {
+
+        XmlMapper xmlMapper = new XmlMapper();
+
+        Object responseBody = null;
+        try {
+            Document xmlDoc = xmlMapper.write( errorsNode, errorsSchemaNode );
+
+            responseBody = documentToString( xmlDoc );
+        }
+        catch( TransformerException | UnsupportedDataTypeException | UnsupportedEncodingException e ) {
+            LOG.error( "Error writing error response body", e );
+        }
+
+        return responseBody;
+    }
+
+    private String documentToString( Document doc ) throws TransformerException, UnsupportedEncodingException {
+        Transformer transformer = createTransformer();
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+
+        transformer.transform( new DOMSource( doc ), new StreamResult( outStream ) );
+
+        return outStream.toString( "UTF-8" );
+    }
+
+    private Transformer createTransformer() throws TransformerFactoryConfigurationError,
+        TransformerConfigurationException {
+        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" );
+        return transformer;
+    }
+
+    private Node<?> toDomNode( RestconfError error ) {
+
+        CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+        builder.setQName( ERROR_LIST_QNAME );
+
+        addLeaf( builder, ERROR_TYPE_QNAME, error.getErrorType().getErrorTypeTag() );
+        addLeaf( builder, ERROR_TAG_QNAME, error.getErrorTag().getTagValue() );
+        addLeaf( builder, ERROR_MESSAGE_QNAME, error.getErrorMessage() );
+        addLeaf( builder, ERROR_APP_TAG_QNAME, error.getErrorAppTag() );
+
+        Node<?> errorInfoNode = parseErrorInfo( error.getErrorInfo() );
+        if( errorInfoNode != null ) {
+            builder.add( errorInfoNode );
+        }
+
+        return builder.toInstance();
+    }
+
+    private Node<?> parseErrorInfo( String errorInfo ) {
+        if( Strings.isNullOrEmpty( errorInfo ) ) {
+            return null;
+        }
+
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware( true );
+        factory.setCoalescing( true );
+        factory.setIgnoringElementContentWhitespace( true );
+        factory.setIgnoringComments( true );
+
+        // Wrap the error info content in a root <error-info> element so it can be parsed
+        // as XML. The error info content may or may not be XML. If not then it will be
+        // parsed as text content of the <error-info> element.
+
+        String errorInfoWithRoot =
+                new StringBuilder( "<error-info xmlns=\"" ).append( NAMESPACE ).append( "\">" )
+                        .append( errorInfo ).append( "</error-info>" ).toString();
+
+        Document doc = null;
+        try {
+            doc = factory.newDocumentBuilder().parse(
+                                 new InputSource( new StringReader( errorInfoWithRoot ) ) );
+        }
+        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() );
+            return null;
+        }
+
+        Node<?> errorInfoNode = XmlDocumentUtils.toDomNode( doc );
+
+        if( errorInfoNode instanceof CompositeNode ) {
+            CompositeNode compositeNode = (CompositeNode)XmlDocumentUtils.toDomNode( doc );
+
+            // At this point the QName for the "error-info" CompositeNode doesn't contain the revision
+            // as it isn't present in the XML. So we'll copy all the child nodes and create a new
+            // CompositeNode with the full QName. This is done so the XML/JSON mapping code can
+            // locate the schema.
+
+            ImmutableList.Builder<Node<?>> childNodes = ImmutableList.builder();
+            for( Entry<QName, List<Node<?>>> entry: compositeNode.entrySet() ) {
+                childNodes.addAll( entry.getValue() );
+            }
+
+            errorInfoNode = ImmutableCompositeNode.create( ERROR_INFO_QNAME, childNodes.build() );
+        }
+
+        return errorInfoNode;
+    }
+
+    private void addLeaf( CompositeNodeBuilder<ImmutableCompositeNode> builder, QName qname,
+                          String value ) {
+        if( !Strings.isNullOrEmpty( value ) ) {
+            builder.addLeaf( qname, value );
+        }
+    }
+}
index 5dba7474caa22affe5d73594ff918de54f1d4c5f..422cf04cca82f7d0bf5d2f535df40a6f7688215e 100644 (file)
@@ -23,7 +23,7 @@ import javax.ws.rs.ext.Provider;
 
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -38,7 +38,7 @@ public enum StructuredDataToJsonProvider implements MessageBodyWriter<Structured
 
     @Override
     public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
-        return true;
+        return type.equals( StructuredData.class );
     }
 
     @Override
@@ -52,7 +52,7 @@ public enum StructuredDataToJsonProvider implements MessageBodyWriter<Structured
             throws IOException, WebApplicationException {
         CompositeNode data = t.getData();
         if (data == null) {
-            throw new ResponseException(Response.Status.NOT_FOUND, "No data exists.");
+            throw new RestconfDocumentedException(Response.Status.NOT_FOUND);
         }
 
         JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, "UTF-8"));
index 7d6b329cfa2ac57a731f94934fa99f3acf04ca9f..bcb3c422ff8dc0c33c8a44d27d20b63ec1e3852d 100644 (file)
@@ -28,7 +28,9 @@ import javax.xml.transform.stream.StreamResult;
 
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -46,7 +48,7 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter<StructuredD
 
     @Override
     public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
-        return true;
+        return type.equals( StructuredData.class );
     }
 
     @Override
@@ -60,9 +62,9 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter<StructuredD
             throws IOException, WebApplicationException {
         CompositeNode data = t.getData();
         if (data == null) {
-            throw new ResponseException(Response.Status.NOT_FOUND, "No data exists.");
+            throw new RestconfDocumentedException(Response.Status.NOT_FOUND);
         }
-        
+
         XmlMapper xmlMapper = new XmlMapper();
         Document domTree = xmlMapper.write(data, (DataNodeContainer) t.getSchema());
         try {
@@ -76,7 +78,8 @@ public enum StructuredDataToXmlProvider implements MessageBodyWriter<StructuredD
             transformer.transform(new DOMSource(domTree), new StreamResult(entityStream));
         } catch (TransformerException e) {
             logger.error("Error during translation of Document to OutputStream", e);
-            throw new ResponseException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
+            throw new RestconfDocumentedException( e.getMessage(), ErrorType.TRANSPORT,
+                                                   ErrorTag.OPERATION_FAILED );
         }
     }
 
index 13d617031ad5717367ee50778c692ccfbda3fb16..bc7473864cbb3ffa553c78fbf8a96fb64a7dad6d 100644 (file)
@@ -16,15 +16,18 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.Provider;
 import javax.xml.stream.XMLStreamException;
 
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Provider
 @Consumes({ Draft02.MediaTypes.DATA + RestconfService.XML, Draft02.MediaTypes.OPERATION + RestconfService.XML,
@@ -32,6 +35,8 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 public enum XmlToCompositeNodeProvider implements MessageBodyReader<CompositeNode> {
     INSTANCE;
 
+    private final static Logger LOG = LoggerFactory.getLogger( XmlToCompositeNodeProvider.class );
+
     @Override
     public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
         return true;
@@ -45,7 +50,10 @@ public enum XmlToCompositeNodeProvider implements MessageBodyReader<CompositeNod
         try {
             return xmlReader.read(entityStream);
         } catch (XMLStreamException | UnsupportedFormatException e) {
-            throw new ResponseException(Response.Status.BAD_REQUEST, e.getMessage());
+            LOG.debug( "Error parsing json input", e );
+            throw new RestconfDocumentedException(
+                            "Error parsing input: " + e.getMessage(),
+                            ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE );
         }
     }
 
index 1cc1f783d676ed69f68c959d1024a58349dc51d9..062d03a49f56e12ca6bb178fa39303b78fc059da 100644 (file)
@@ -18,7 +18,8 @@ import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
 import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
-import org.opendaylight.controller.sal.rest.impl.RestconfProvider;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -53,9 +54,7 @@ public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNod
 
     private void checkPreconditions() {
         if( context == null || dataService == null ) {
-            ResponseException _responseException = new ResponseException( Status.SERVICE_UNAVAILABLE,
-                    RestconfProvider.NOT_INITALIZED_MSG );
-            throw _responseException;
+            throw new RestconfDocumentedException( Status.SERVICE_UNAVAILABLE );
         }
     }
 
@@ -95,17 +94,10 @@ public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNod
         return mountPoint.readOperationalData( path );
     }
 
-    public RpcResult<CompositeNode> invokeRpc( final QName type, final CompositeNode payload ) {
+    public Future<RpcResult<CompositeNode>> invokeRpc( final QName type, final CompositeNode payload ) {
         this.checkPreconditions();
 
-        final Future<RpcResult<CompositeNode>> future = context.rpc( type, payload );
-
-        try {
-            return future.get();
-        }
-        catch( Exception e ) {
-            throw new ResponseException( e, "Error invoking RPC " + type );
-        }
+        return context.rpc( type, payload );
     }
 
     public Future<RpcResult<TransactionStatus>> commitConfigurationDataPut( final InstanceIdentifier path,
@@ -138,9 +130,9 @@ public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNod
         if (availableNode != null) {
             String errMsg = "Post Configuration via Restconf was not executed because data already exists";
             BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
-            // FIXME: return correct ietf-restconf:errors -> follow specification
-            // (http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48)
-            throw new ResponseException(Status.CONFLICT, errMsg);
+
+            throw new RestconfDocumentedException(
+                    "Data already exists for path: " + path, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS );
         }
         BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path );
         transaction.putConfigurationData( path, payload );
@@ -157,9 +149,9 @@ public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNod
         if (availableNode != null) {
             String errMsg = "Post Configuration via Restconf was not executed because data already exists";
             BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
-            // FIXME: return correct ietf-restconf:errors -> follow specification
-            // (http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48)
-            throw new ResponseException(Status.CONFLICT, errMsg);
+
+            throw new RestconfDocumentedException(
+                    "Data already exists for path: " + path, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS );
         }
         BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path );
         transaction.putConfigurationData( path, payload );
index 1c076d1e2e76291f999384ccc2704a910400812e..86ed13a2802bc98b4ddd9a259aa5ac1c329118e2 100644 (file)
@@ -38,11 +38,12 @@ import javax.ws.rs.core.Response.Status;
 
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.core.api.mount.MountService;
+import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.impl.RestUtil;
-import org.opendaylight.controller.sal.rest.impl.RestconfProvider;
 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
 import org.opendaylight.controller.sal.restconf.impl.RestCodec;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
@@ -55,6 +56,7 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
@@ -108,7 +110,7 @@ public class ControllerContext implements SchemaContextListener {
 
     private void checkPreconditions() {
         if( globalSchema == null ) {
-            throw new ResponseException( Status.SERVICE_UNAVAILABLE, RestconfProvider.NOT_INITALIZED_MSG );
+            throw new RestconfDocumentedException( Status.SERVICE_UNAVAILABLE );
         }
     }
 
@@ -139,8 +141,9 @@ public class ControllerContext implements SchemaContextListener {
         String first = pathArgs.iterator().next();
         final String startModule = ControllerContext.toModuleName( first );
         if( startModule == null ) {
-            throw new ResponseException( Status.BAD_REQUEST,
-                    "First node in URI has to be in format \"moduleName:nodeName\"" );
+            throw new RestconfDocumentedException(
+                    "First node in URI has to be in format \"moduleName:nodeName\"",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         InstanceIdentifierBuilder builder = InstanceIdentifier.builder();
@@ -149,7 +152,8 @@ public class ControllerContext implements SchemaContextListener {
                                                            latestModule, null, toMountPointIdentifier );
 
         if( iiWithSchemaNode == null ) {
-            throw new ResponseException( Status.BAD_REQUEST, "URI has bad format" );
+            throw new RestconfDocumentedException(
+                    "URI has bad format", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         return iiWithSchemaNode;
@@ -386,6 +390,112 @@ public class ControllerContext implements SchemaContextListener {
         return builder.toString();
     }
 
+    public Module getRestconfModule() {
+        return findModuleByNameAndRevision( Draft02.RestConfModule.IETF_RESTCONF_QNAME );
+    }
+
+    public DataSchemaNode getRestconfModuleErrorsSchemaNode() {
+        Module restconfModule = getRestconfModule();
+        if( restconfModule == null ) {
+            return null;
+        }
+
+        Set<GroupingDefinition> groupings = restconfModule.getGroupings();
+
+        final Predicate<GroupingDefinition> filter = new Predicate<GroupingDefinition>() {
+            @Override
+            public boolean apply(final GroupingDefinition g) {
+                return Objects.equal(g.getQName().getLocalName(),
+                                     Draft02.RestConfModule.ERRORS_GROUPING_SCHEMA_NODE);
+            }
+        };
+
+        Iterable<GroupingDefinition> filteredGroups = Iterables.filter(groupings, filter);
+
+        final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null);
+
+        List<DataSchemaNode> instanceDataChildrenByName =
+                this.findInstanceDataChildrenByName(restconfGrouping,
+                                                    Draft02.RestConfModule.ERRORS_CONTAINER_SCHEMA_NODE);
+        return Iterables.getFirst(instanceDataChildrenByName, null);
+    }
+
+    public DataSchemaNode getRestconfModuleRestConfSchemaNode( Module inRestconfModule,
+                                                               String schemaNodeName ) {
+        Module restconfModule = inRestconfModule;
+        if( restconfModule == null ) {
+            restconfModule = getRestconfModule();
+        }
+
+        if( restconfModule == null ) {
+            return null;
+        }
+
+        Set<GroupingDefinition> groupings = restconfModule.getGroupings();
+
+        final Predicate<GroupingDefinition> filter = new Predicate<GroupingDefinition>() {
+            @Override
+            public boolean apply(final GroupingDefinition g) {
+                return Objects.equal(g.getQName().getLocalName(),
+                                     Draft02.RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE);
+            }
+        };
+
+        Iterable<GroupingDefinition> filteredGroups = Iterables.filter(groupings, filter);
+
+        final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null);
+
+        List<DataSchemaNode> instanceDataChildrenByName =
+                this.findInstanceDataChildrenByName(restconfGrouping,
+                                                            Draft02.RestConfModule.RESTCONF_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode restconfContainer = Iterables.getFirst(instanceDataChildrenByName, null);
+
+        if (Objects.equal(schemaNodeName, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE)) {
+            List<DataSchemaNode> instances =
+                    this.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+                                                    Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
+            return Iterables.getFirst(instances, null);
+        }
+        else if(Objects.equal(schemaNodeName, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE)) {
+            List<DataSchemaNode> instances =
+                    this.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+                                                   Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
+            return Iterables.getFirst(instances, null);
+        }
+        else if(Objects.equal(schemaNodeName, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE)) {
+            List<DataSchemaNode> instances =
+                    this.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+                                                   Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
+            final DataSchemaNode modules = Iterables.getFirst(instances, null);
+            instances = this.findInstanceDataChildrenByName(((DataNodeContainer) modules),
+                                                               Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
+            return Iterables.getFirst(instances, null);
+        }
+        else if(Objects.equal(schemaNodeName, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE)) {
+            List<DataSchemaNode> instances =
+                    this.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+                                                         Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
+            return Iterables.getFirst(instances, null);
+        }
+        else if(Objects.equal(schemaNodeName, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE)) {
+            List<DataSchemaNode> instances =
+                    this.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+                                                         Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
+            final DataSchemaNode modules = Iterables.getFirst(instances, null);
+            instances = this.findInstanceDataChildrenByName(((DataNodeContainer) modules),
+                                                                 Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
+            return Iterables.getFirst(instances, null);
+        }
+        else if(Objects.equal(schemaNodeName, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE)) {
+            List<DataSchemaNode> instances =
+                    this.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+                                                   Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
+            return Iterables.getFirst(instances, null);
+        }
+
+        return null;
+    }
+
     private static DataSchemaNode childByQName( final ChoiceNode container, final QName name ) {
         for( final ChoiceCaseNode caze : container.getCases() ) {
             final DataSchemaNode ret = ControllerContext.childByQName( caze, name );
@@ -461,27 +571,30 @@ public class ControllerContext implements SchemaContextListener {
             if( Objects.equal( moduleName, ControllerContext.MOUNT_MODULE ) &&
                 Objects.equal( nodeName, ControllerContext.MOUNT_NODE ) ) {
                 if( mountPoint != null ) {
-                    throw new ResponseException( Status.BAD_REQUEST,
-                                         "Restconf supports just one mount point in URI." );
+                    throw new RestconfDocumentedException(
+                            "Restconf supports just one mount point in URI.",
+                            ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED );
                 }
 
                 if( mountService == null ) {
-                    throw new ResponseException( Status.SERVICE_UNAVAILABLE,
-                                "MountService was not found. Finding behind mount points does not work." );
+                    throw new RestconfDocumentedException(
+                            "MountService was not found. Finding behind mount points does not work.",
+                            ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED );
                 }
 
                 final InstanceIdentifier partialPath = builder.toInstance();
                 final MountInstance mount = mountService.getMountPoint( partialPath );
                 if( mount == null ) {
                     LOG.debug( "Instance identifier to missing mount point: {}", partialPath );
-                    throw new ResponseException( Status.BAD_REQUEST,
-                                                 "Mount point does not exist." );
+                    throw new RestconfDocumentedException(
+                         "Mount point does not exist.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
                 }
 
                 final SchemaContext mountPointSchema = mount.getSchemaContext();
                 if( mountPointSchema == null ) {
-                    throw new ResponseException( Status.BAD_REQUEST,
-                                       "Mount point does not contain any schema with modules." );
+                    throw new RestconfDocumentedException(
+                            "Mount point does not contain any schema with modules.",
+                            ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
                 }
 
                 if( returnJustMountPoint ) {
@@ -496,16 +609,17 @@ public class ControllerContext implements SchemaContextListener {
 
                 final String moduleNameBehindMountPoint = toModuleName(  strings.get( 1 ) );
                 if( moduleNameBehindMountPoint == null ) {
-                    throw new ResponseException( Status.BAD_REQUEST,
-                            "First node after mount point in URI has to be in format \"moduleName:nodeName\"" );
+                    throw new RestconfDocumentedException(
+                        "First node after mount point in URI has to be in format \"moduleName:nodeName\"",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                 }
 
                 final Module moduleBehindMountPoint = this.getLatestModule( mountPointSchema,
                                                                             moduleNameBehindMountPoint );
                 if( moduleBehindMountPoint == null ) {
-                    throw new ResponseException( Status.BAD_REQUEST,
-                                                 "URI has bad format. \"" + moduleName +
-                                                 "\" module does not exist in mount point." );
+                    throw new RestconfDocumentedException(
+                            "\"" +moduleName + "\" module does not exist in mount point.",
+                            ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
                 }
 
                 List<String> subList = strings.subList( 1, strings.size() );
@@ -517,8 +631,9 @@ public class ControllerContext implements SchemaContextListener {
             if( mountPoint == null ) {
                 module = this.getLatestModule( globalSchema, moduleName );
                 if( module == null ) {
-                    throw new ResponseException( Status.BAD_REQUEST,
-                            "URI has bad format. \"" + moduleName + "\" module does not exist." );
+                    throw new RestconfDocumentedException(
+                            "\"" + moduleName + "\" module does not exist.",
+                            ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
                 }
             }
             else {
@@ -526,20 +641,20 @@ public class ControllerContext implements SchemaContextListener {
                 module = schemaContext == null ? null :
                                           this.getLatestModule( schemaContext, moduleName );
                 if( module == null ) {
-                    throw new ResponseException( Status.BAD_REQUEST,
-                                        "URI has bad format. \"" + moduleName +
-                                        "\" module does not exist in mount point." );
+                    throw new RestconfDocumentedException(
+                            "\"" + moduleName + "\" module does not exist in mount point.",
+                            ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
                 }
             }
 
             targetNode = this.findInstanceDataChildByNameAndNamespace(
                                           parentNode, nodeName, module.getNamespace() );;
             if( targetNode == null ) {
-                throw new ResponseException( Status.BAD_REQUEST,
-                            "URI has bad format. Possible reasons:\n" +
-                            "1. \"" + head + "\" was not found in parent data node.\n" +
-                            "2. \"" + head + "\" is behind mount point. Then it should be in format \"/" +
-                            MOUNT + "/" + head + "\"." );
+                throw new RestconfDocumentedException(
+                        "URI has bad format. Possible reasons:\n" +
+                        1. \"" + head + "\" was not found in parent data node.\n" +
+                        2. \"" + head + "\" is behind mount point. Then it should be in format \"/" +
+                        MOUNT + "/" + head + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
         } else {
             final List<DataSchemaNode> potentialSchemaNodes =
@@ -552,26 +667,27 @@ public class ControllerContext implements SchemaContextListener {
                               .append( "\n" );
                 }
 
-                throw new ResponseException( Status.BAD_REQUEST,
+                throw new RestconfDocumentedException(
                         "URI has bad format. Node \"" + nodeName +
                         "\" is added as augment from more than one module. " +
                         "Therefore the node must have module name and it has to be in format \"moduleName:nodeName\"." +
                         "\nThe node is added as augment from modules with namespaces:\n" +
-                        strBuilder.toString() );
+                        strBuilder.toString(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
             if( potentialSchemaNodes.isEmpty() ) {
-                throw new ResponseException( Status.BAD_REQUEST, "URI has bad format. \"" + nodeName +
-                                             "\" was not found in parent data node.\n" );
+                throw new RestconfDocumentedException(
+                        "\"" + nodeName + "\" in URI was not found in parent data node",
+                        ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
             }
 
             targetNode = potentialSchemaNodes.iterator().next();
         }
 
         if( !this.isListOrContainer( targetNode ) ) {
-            throw new ResponseException( Status.BAD_REQUEST,
-                                         "URI has bad format. Node \"" + head +
-                                         "\" must be Container or List yang type." );
+            throw new RestconfDocumentedException(
+                    "URI has bad format. Node \"" + head + "\" must be Container or List yang type.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         int consumed = 1;
@@ -579,8 +695,9 @@ public class ControllerContext implements SchemaContextListener {
             final ListSchemaNode listNode = ((ListSchemaNode) targetNode);
             final int keysSize = listNode.getKeyDefinition().size();
             if( (strings.size() - consumed) < keysSize ) {
-                throw new ResponseException( Status.BAD_REQUEST, "Missing key for list \"" +
-                                             listNode.getQName().getLocalName() + "\"." );
+                throw new RestconfDocumentedException(
+                        "Missing key for list \"" + listNode.getQName().getLocalName() + "\".",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
             final List<String> uriKeyValues = strings.subList( consumed, consumed + keysSize );
@@ -590,9 +707,10 @@ public class ControllerContext implements SchemaContextListener {
                 {
                     final String uriKeyValue = uriKeyValues.get( i );
                     if( uriKeyValue.equals( NULL_VALUE ) ) {
-                        throw new ResponseException( Status.BAD_REQUEST,
-                                    "URI has bad format. List \"" + listNode.getQName().getLocalName() +
-                                    "\" cannot contain \"null\" value as a key." );
+                        throw new RestconfDocumentedException(
+                                "URI has bad format. List \"" + listNode.getQName().getLocalName() +
+                                "\" cannot contain \"null\" value as a key.",
+                                ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                     }
 
                     this.addKeyValue( keyValues, listNode.getDataChildByName( key ),
@@ -711,8 +829,9 @@ public class ControllerContext implements SchemaContextListener {
         }
 
         if( decoded == null ) {
-            throw new ResponseException( Status.BAD_REQUEST, uriValue + " from URI can\'t be resolved. " +
-                                         additionalInfo );
+            throw new RestconfDocumentedException(
+                            uriValue + " from URI can't be resolved. " + additionalInfo,
+                            ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         map.put( node.getQName(), decoded );
@@ -807,8 +926,9 @@ public class ControllerContext implements SchemaContextListener {
             return decodedPathArgs;
         }
         catch( UnsupportedEncodingException e ) {
-            throw new ResponseException( Status.BAD_REQUEST,
-                    "Invalid URL path '" + strings + "': " + e.getMessage() );
+            throw new RestconfDocumentedException(
+                    "Invalid URL path '" + strings + "': " + e.getMessage(),
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
     }
 
@@ -818,8 +938,9 @@ public class ControllerContext implements SchemaContextListener {
                 return URLDecoder.decode( pathArg, URI_ENCODING_CHAR_SET );
             }
             catch( UnsupportedEncodingException e ) {
-                throw new ResponseException( Status.BAD_REQUEST,
-                        "Invalid URL path arg '" + pathArg + "': " + e.getMessage() );
+                throw new RestconfDocumentedException(
+                        "Invalid URL path arg '" + pathArg + "': " + e.getMessage(),
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
         }
 
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ResponseException.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ResponseException.java
deleted file mode 100644 (file)
index 007fb8e..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.restconf.impl;
-
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-
-public class ResponseException extends WebApplicationException {
-
-    private static final long serialVersionUID = -5320114450593021655L;
-
-    public ResponseException(Status status, String msg) {
-        super(Response.status(status).type(MediaType.TEXT_PLAIN_TYPE).entity(msg).build());
-    }
-
-    public ResponseException(Throwable cause, String msg) {
-        super(cause, Response.status(Status.INTERNAL_SERVER_ERROR).
-                                         type(MediaType.TEXT_PLAIN_TYPE).entity(msg).build());
-    }
-}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java
new file mode 100644 (file)
index 0000000..0548e95
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.restconf.impl;
+
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response.Status;
+
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Unchecked exception to communicate error information, as defined in the ietf restcong draft,
+ * to be sent to the client.
+ *
+ * @author Devin Avery
+ * @author Thomas Pantelis
+ * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02}
+ */
+public class RestconfDocumentedException extends WebApplicationException {
+
+    private static final long serialVersionUID = 1L;
+
+    private final List<RestconfError> errors;
+    private final Status status;
+
+    /**
+     * Constructs an instance with an error message. The error type defaults to APPLICATION and
+     * the error tag defaults to OPERATION_FAILED.
+     *
+     * @param message A string which provides a plain text string describing the error.
+     */
+    public RestconfDocumentedException( String message ) {
+        this( message, RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED );
+    }
+
+    /**
+     * Constructs an instance with an error message, error type, and error tag.
+     *
+     * @param message A string which provides a plain text string describing the error.
+     * @param errorType The enumerated type indicating the layer where the error occurred.
+     * @param errorTag The enumerated tag representing a more specific error cause.
+     */
+    public RestconfDocumentedException( String message, ErrorType errorType, ErrorTag errorTag ) {
+        this( null, new RestconfError( errorType, errorTag, message ) );
+    }
+
+    /**
+     * Constructs an instance with an error message and exception cause. The stack trace of the
+     * exception is included in the error info.
+     *
+     * @param message A string which provides a plain text string describing the error.
+     * @param cause The underlying exception cause.
+     */
+    public RestconfDocumentedException( String message, Throwable cause ) {
+        this( cause, new RestconfError( RestconfError.ErrorType.APPLICATION,
+                                        RestconfError.ErrorTag.OPERATION_FAILED, message,
+                                        null, RestconfError.toErrorInfo( cause ) ) );
+    }
+
+    /**
+     * Constructs an instance with the given error.
+     */
+    public RestconfDocumentedException( RestconfError error ) {
+        this( null, error );
+    }
+
+    /**
+     * Constructs an instance with the given errors.
+     */
+    public RestconfDocumentedException( List<RestconfError> errors ) {
+        this.errors = ImmutableList.copyOf( errors );
+        Preconditions.checkArgument( !this.errors.isEmpty(), "RestconfError list can't be empty" );
+        status = null;
+    }
+
+    /**
+     * Constructs an instance with an HTTP status and no error information.
+     *
+     * @param status the HTTP status.
+     */
+    public RestconfDocumentedException( Status status ) {
+        Preconditions.checkNotNull( status, "Status can't be null" );
+        errors = ImmutableList.of();
+        this.status = status;
+    }
+
+    private RestconfDocumentedException( Throwable cause, RestconfError error ) {
+        super( cause );
+        Preconditions.checkNotNull( error, "RestconfError can't be null" );
+        errors = ImmutableList.of( error );
+        status = null;
+    }
+
+    public List<RestconfError> getErrors() {
+        return errors;
+    }
+
+    public Status getStatus() {
+        return status;
+    }
+
+
+    @Override
+    public String getMessage() {
+        return "errors: " + errors + (status != null ? ", status: " + status : "");
+    }
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfError.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfError.java
new file mode 100644 (file)
index 0000000..9220f8b
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+* Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v1.0 which accompanies this distribution,
+* and is available at http://www.eclipse.org/legal/epl-v10.html
+*/
+package org.opendaylight.controller.sal.restconf.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Encapsulates a restconf error as defined in the ietf restconf draft.
+ *
+ * <br><br><b>Note:</b> Enumerations defined within are provided by the ietf restconf draft.
+ *
+ * @author Devin Avery
+ * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02}
+ */
+public class RestconfError {
+
+    public static enum ErrorType {
+        /** Errors relating to the transport layer */
+        TRANSPORT,
+        /** Errors relating to the RPC or notification layer */
+        RPC,
+        /** Errors relating to the protocol operation layer. */
+        PROTOCOL,
+        /** Errors relating to the server application layer. */
+        APPLICATION;
+
+        public String getErrorTypeTag() {
+            return name().toLowerCase();
+        }
+
+        public static ErrorType valueOfCaseInsensitive( String value )
+        {
+            try {
+                return ErrorType.valueOf( ErrorType.class, value.toUpperCase() );
+            }
+            catch( IllegalArgumentException e ) {
+                return APPLICATION;
+            }
+        }
+    }
+
+    public static enum ErrorTag {
+        IN_USE( "in-use", Status.fromStatusCode(409)),
+        INVALID_VALUE( "invalid-value", Status.fromStatusCode(400)),
+        TOO_BIG( "too-big", Status.fromStatusCode(413)),
+        MISSING_ATTRIBUTE( "missing-attribute", Status.fromStatusCode(400)),
+        BAD_ATTRIBUTE( "bad-attribute", Status.fromStatusCode(400)),
+        UNKNOWN_ATTRIBUTE( "unknown-attribute", Status.fromStatusCode(400)),
+        BAD_ELEMENT( "bad-element", Status.fromStatusCode(400)),
+        UNKNOWN_ELEMENT( "unknown-element", Status.fromStatusCode(400)),
+        UNKNOWN_NAMESPACE( "unknown-namespace", Status.fromStatusCode(400)),
+        ACCESS_DENIED( "access-denied", Status.fromStatusCode(403)),
+        LOCK_DENIED( "lock-denied", Status.fromStatusCode(409)),
+        RESOURCE_DENIED( "resource-denied", Status.fromStatusCode(409)),
+        ROLLBACK_FAILED( "rollback-failed", Status.fromStatusCode(500)),
+        DATA_EXISTS( "data-exists", Status.fromStatusCode(409)),
+        DATA_MISSING( "data-missing", Status.fromStatusCode(409)),
+        OPERATION_NOT_SUPPORTED( "operation-not-supported", Status.fromStatusCode(501)),
+        OPERATION_FAILED( "operation-failed", Status.fromStatusCode(500)),
+        PARTIAL_OPERATION( "partial-operation", Status.fromStatusCode(500)),
+        MALFORMED_MESSAGE( "malformed-message", Status.fromStatusCode(400));
+
+        private final String tagValue;
+        private final Status statusCode;
+
+        ErrorTag(final String tagValue, final Status statusCode) {
+            this.tagValue = tagValue;
+            this.statusCode = statusCode;
+        }
+
+        public String getTagValue() {
+            return this.tagValue.toLowerCase();
+        }
+
+        public static ErrorTag valueOfCaseInsensitive( String value )
+        {
+            try {
+                return ErrorTag.valueOf( ErrorTag.class, value.toUpperCase().replaceAll( "-","_" ) );
+            }
+            catch( IllegalArgumentException e ) {
+                return OPERATION_FAILED;
+            }
+        }
+
+        public Status getStatusCode() {
+            return statusCode;
+        }
+    }
+
+    private final ErrorType errorType;
+    private final ErrorTag errorTag;
+    private final String errorInfo;
+    private final String errorAppTag;
+    private final String errorMessage;
+    //TODO: Add in the error-path concept as defined in the ietf draft.
+
+    static String toErrorInfo( Throwable cause ) {
+        StringWriter writer = new StringWriter();
+        cause.printStackTrace( new PrintWriter( writer ) );
+        return writer.toString();
+    }
+
+    /**
+     * Constructs a RestConfError
+     *
+     * @param errorType The enumerated type indicating the layer where the error occurred.
+     * @param errorTag The enumerated tag representing a more specific error cause.
+     * @param errorMessage A string which provides a plain text string describing the error.
+     */
+    public RestconfError(ErrorType errorType, ErrorTag errorTag, String errorMessage) {
+        this( errorType, errorTag, errorMessage, null );
+    }
+
+    /**
+     * Constructs a RestConfError object.
+     *
+     * @param errorType The enumerated type indicating the layer where the error occurred.
+     * @param errorTag The enumerated tag representing a more specific error cause.
+     * @param errorMessage A string which provides a plain text string describing the error.
+     * @param errorAppTag A string which represents an application-specific error tag that further
+     *                    specifies the error cause.
+     */
+    public RestconfError(ErrorType errorType, ErrorTag errorTag, String errorMessage,
+                         String errorAppTag) {
+        this( errorType, errorTag, errorMessage, errorAppTag, null );
+    }
+
+    /**
+     * Constructs a RestConfError object.
+     *
+     * @param errorType The enumerated type indicating the layer where the error occurred.
+     * @param errorTag The enumerated tag representing a more specific error cause.
+     * @param errorMessage A string which provides a plain text string describing the error.
+     * @param errorAppTag A string which represents an application-specific error tag that further
+     *                    specifies the error cause.
+     * @param errorInfo A string, <b>formatted as XML</b>, which contains additional error information.
+     */
+    public RestconfError(ErrorType errorType, ErrorTag errorTag, String errorMessage,
+                         String errorAppTag, String errorInfo) {
+        Preconditions.checkNotNull( errorType, "Error type is required for RestConfError" );
+        Preconditions.checkNotNull( errorTag, "Error tag is required for RestConfError");
+        this.errorType = errorType;
+        this.errorTag = errorTag;
+        this.errorMessage = errorMessage;
+        this.errorAppTag = errorAppTag;
+        this.errorInfo = errorInfo;
+    }
+
+    /**
+     * Constructs a RestConfError object from an RpcError.
+     */
+    public RestconfError( RpcError rpcError ) {
+
+        this.errorType = rpcError.getErrorType() == null ? ErrorType.APPLICATION :
+                               ErrorType.valueOfCaseInsensitive( rpcError.getErrorType().name() );
+
+        this.errorTag = rpcError.getTag() == null ? ErrorTag.OPERATION_FAILED :
+                                    ErrorTag.valueOfCaseInsensitive( rpcError.getTag().toString() );
+
+        this.errorMessage = rpcError.getMessage();
+        this.errorAppTag = rpcError.getApplicationTag();
+
+        String errorInfo = null;
+        if( rpcError.getInfo() == null ) {
+            if( rpcError.getCause() != null ) {
+                errorInfo = toErrorInfo( rpcError.getCause() );
+            }
+            else if( rpcError.getSeverity() != null ) {
+                errorInfo = "<severity>" + rpcError.getSeverity().toString().toLowerCase() +
+                            "</severity>";
+            }
+        }
+        else {
+            errorInfo = rpcError.getInfo();
+        }
+
+        this.errorInfo = errorInfo;
+    }
+
+    public ErrorType getErrorType() {
+        return errorType;
+    }
+
+    public ErrorTag getErrorTag() {
+        return errorTag;
+    }
+
+    public String getErrorInfo() {
+        return errorInfo;
+    }
+
+    public String getErrorAppTag() {
+        return errorAppTag;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    @Override
+    public String toString() {
+        return "error-type: " + errorType.getErrorTypeTag()
+                + ", error-tag: " + errorTag.getTagValue() + ", "
+                + (errorAppTag != null ? "error-app-tag: " + errorAppTag + ", " : "")
+                + (errorMessage != null ? "error-message: " + errorMessage : "")
+                + (errorInfo != null ? "error-info: " + errorInfo + ", " : "") + "]";
+    }
+
+}
\ No newline at end of file
index e9d489dd3523ceab030d4473dd6fb9f62dd662f6..ad682bc8291d50e52d674607e30142009bac459e 100644 (file)
@@ -8,11 +8,19 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+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.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -28,15 +36,29 @@ 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;
+import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
+import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.RestCodec;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.StructuredData;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.controller.sal.streams.listeners.Notificator;
 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
 import org.opendaylight.yangtools.concepts.Codec;
 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;
@@ -49,7 +71,6 @@ import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
-import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
@@ -63,15 +84,6 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
 
-import com.google.common.base.Objects;
-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.Iterables;
-import com.google.common.collect.Lists;
-
-@SuppressWarnings("all")
 public class RestconfImpl implements RestconfService {
     private final static RestconfImpl INSTANCE = new RestconfImpl();
 
@@ -81,26 +93,6 @@ public class RestconfImpl implements RestconfService {
 
     private final static SimpleDateFormat REVISION_FORMAT =  new SimpleDateFormat("yyyy-MM-dd");
 
-    private final static String RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19";
-
-    private final static String RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf";
-
-    private final static String RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules";
-
-    private final static String RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module";
-
-    private final static String RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE = "streams";
-
-    private final static String RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE = "stream";
-
-    private final static String RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations";
-
     private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
 
     private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
@@ -129,8 +121,8 @@ public class RestconfImpl implements RestconfService {
         final Module restconfModule = this.getRestconfModule();
 
         final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
-        final DataSchemaNode moduleSchemaNode =
-                this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                        restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
 
         Set<Module> allModules = this.controllerContext.getAllModules();
         for (final Module module : allModules) {
@@ -138,8 +130,8 @@ public class RestconfImpl implements RestconfService {
             modulesAsData.add(moduleCompositeNode);
         }
 
-        final DataSchemaNode modulesSchemaNode =
-                this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                   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);
@@ -151,14 +143,14 @@ public class RestconfImpl implements RestconfService {
 
         final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode streamSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE);
+        final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                             restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
         for (final String streamName : availableStreams) {
             streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
         }
 
-        final DataSchemaNode streamsSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                     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);
@@ -175,22 +167,22 @@ public class RestconfImpl implements RestconfService {
             modules = this.controllerContext.getAllModules(mountPoint);
         }
         else {
-            throw new ResponseException(Status.BAD_REQUEST,
-                "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
-                ControllerContext.MOUNT);
+            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 );
         }
 
         final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode moduleSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                         restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
 
         for (final Module module : modules) {
             modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
         }
 
-        final DataSchemaNode modulesSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                  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);
@@ -212,14 +204,15 @@ public class RestconfImpl implements RestconfService {
         }
 
         if (module == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
+            throw new RestconfDocumentedException(
                     "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
-                    moduleNameAndRevision.getRevision() + "' was not found.");
+                    moduleNameAndRevision.getRevision() + "' was not found.",
+                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode moduleSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+        final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                                          restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
         final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
         return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
     }
@@ -241,9 +234,9 @@ public class RestconfImpl implements RestconfService {
             modules = this.controllerContext.getAllModules(mountPoint);
         }
         else {
-            throw new ResponseException(Status.BAD_REQUEST,
-                "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
-            ControllerContext.MOUNT);
+            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 );
         }
 
         return this.operationsFromModulesToStructuredData(modules, mountPoint);
@@ -253,12 +246,12 @@ public class RestconfImpl implements RestconfService {
                                                                  final MountInstance mountPoint) {
         final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
         Module restconfModule = this.getRestconfModule();
-        final DataSchemaNode operationsSchemaNode =
-            this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE);
+        final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
+                              restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
         QName qName = operationsSchemaNode.getQName();
         SchemaPath path = operationsSchemaNode.getPath();
         ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
-                             new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_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();
@@ -286,11 +279,11 @@ public class RestconfImpl implements RestconfService {
     }
 
     private Module getRestconfModule() {
-        QName qName = QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
-                                   RESTCONF_MODULE_DRAFT02_NAME);
-        final Module restconfModule = this.controllerContext.findModuleByNameAndRevision(qName);
+        Module restconfModule = controllerContext.getRestconfModule();
         if (restconfModule == null) {
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR, "Restconf module was not found.");
+            throw new RestconfDocumentedException(
+                    "ietf-restconf module was not found.", ErrorType.APPLICATION,
+                    ErrorTag.OPERATION_NOT_SUPPORTED );
         }
 
         return restconfModule;
@@ -310,8 +303,9 @@ public class RestconfImpl implements RestconfService {
         Iterable<String> split = splitter.split(moduleNameAndRevision);
         final List<String> pathArgs = Lists.<String>newArrayList(split);
         if (pathArgs.size() < 2) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'");
+            throw new RestconfDocumentedException(
+                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         try {
@@ -321,7 +315,9 @@ public class RestconfImpl implements RestconfService {
             return QName.create(null, moduleRevision, moduleName);
         }
         catch (ParseException e) {
-            throw new ResponseException(Status.BAD_REQUEST, "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'");
+            throw new RestconfDocumentedException(
+                    "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
     }
 
@@ -393,66 +389,6 @@ public class RestconfImpl implements RestconfService {
         return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
     }
 
-    private DataSchemaNode getSchemaNode(final Module restconfModule, final String schemaNodeName) {
-        Set<GroupingDefinition> groupings = restconfModule.getGroupings();
-
-        final Predicate<GroupingDefinition> filter = new Predicate<GroupingDefinition>() {
-            @Override
-            public boolean apply(final GroupingDefinition g) {
-                return Objects.equal(g.getQName().getLocalName(),
-                                     RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE);
-            }
-        };
-
-        Iterable<GroupingDefinition> filteredGroups = Iterables.filter(groupings, filter);
-
-        final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null);
-
-        List<DataSchemaNode> instanceDataChildrenByName =
-                this.controllerContext.findInstanceDataChildrenByName(restconfGrouping,
-                                                            RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE);
-        final DataSchemaNode restconfContainer = Iterables.getFirst(instanceDataChildrenByName, null);
-
-        if (Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                    RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                   RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                   RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
-            final DataSchemaNode modules = Iterables.getFirst(instances, null);
-            instances = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) modules),
-                                                               RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                         RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-        else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)) {
-            List<DataSchemaNode> instances =
-                    this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
-                                                         RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
-            final DataSchemaNode modules = Iterables.getFirst(instances, null);
-            instances = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) modules),
-                                                                 RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
-            return Iterables.getFirst(instances, null);
-        }
-
-        return null;
-    }
-
     @Override
     public Object getRoot() {
         return null;
@@ -480,8 +416,9 @@ public class RestconfImpl implements RestconfService {
         final Object pathValue = pathNode == null ? null : pathNode.getValue();
 
         if (!(pathValue instanceof InstanceIdentifier)) {
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
-                                         "Instance identifier was not normalized correctly.");
+            throw new RestconfDocumentedException(
+                    "Instance identifier was not normalized correctly.",
+                    ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
         }
 
         final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
@@ -492,8 +429,9 @@ public class RestconfImpl implements RestconfService {
         }
 
         if (Strings.isNullOrEmpty(streamName)) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                     "Path is empty or contains data node which is not Container or List build-in type.");
+            throw new RestconfDocumentedException(
+                    "Path is empty or contains data node which is not Container or List build-in type.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
@@ -514,8 +452,8 @@ public class RestconfImpl implements RestconfService {
     @Override
     public StructuredData invokeRpc(final String identifier, final String noPayload) {
         if (StringUtils.isNotBlank(noPayload)) {
-            throw new ResponseException(
-                    Status.UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
+            throw new RestconfDocumentedException(
+                    "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
         final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier);
         return callRpc(rpc, null);
@@ -540,7 +478,8 @@ public class RestconfImpl implements RestconfService {
                     .format("Identifier %n%s%ncan\'t contain slash "
                             + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
                             identifier);
-            throw new ResponseException(Status.NOT_FOUND, slashErrorMsg);
+            throw new RestconfDocumentedException(
+                    slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         } else {
             identifierEncoded = identifier;
         }
@@ -549,7 +488,8 @@ public class RestconfImpl implements RestconfService {
         RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
 
         if (rpc == null) {
-            throw new ResponseException(Status.NOT_FOUND, "RPC does not exist.");
+            throw new RestconfDocumentedException(
+                    "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         if (mountPoint == null) {
@@ -562,7 +502,8 @@ public class RestconfImpl implements RestconfService {
 
     private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload) {
         if (rpcExecutor == null) {
-            throw new ResponseException(Status.NOT_FOUND, "RPC does not exist.");
+            throw new RestconfDocumentedException(
+                    "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         CompositeNode rpcRequest = null;
@@ -595,9 +536,20 @@ public class RestconfImpl implements RestconfService {
 
     private void checkRpcSuccessAndThrowException(RpcResult<CompositeNode> rpcResult) {
         if (rpcResult.isSuccessful() == false) {
-            //TODO: Get smart about what error code we are return (Future Bug coming)
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
-                        "The operation was not successful and there were no RPC errors returned");
+
+            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 );
+            }
+
+            List<RestconfError> errorList = Lists.newArrayList();
+            for( RpcError rpcError: rpcErrors ) {
+                errorList.add( new RestconfError( rpcError ) );
+            }
+
+            throw new RestconfDocumentedException( errorList );
         }
     }
 
@@ -647,7 +599,7 @@ public class RestconfImpl implements RestconfService {
             }
         }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error updating data" );
+            throw new RestconfDocumentedException( "Error updating data", e );
         }
 
         if( status.getResult() == TransactionStatus.COMMITED )
@@ -660,8 +612,9 @@ public class RestconfImpl implements RestconfService {
     public Response createConfigurationData(final String identifier, final CompositeNode payload) {
         URI payloadNS = this.namespace(payload);
         if (payloadNS == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
+            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 );
         }
 
         InstanceIdWithSchemaNode iiWithData = null;
@@ -670,9 +623,10 @@ public class RestconfImpl implements RestconfService {
              // payload represents mount point data and URI represents path to the mount point
 
             if (this.endsWithMountPoint(identifier)) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                            "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
-                            "\" for POST operation.");
+                throw new RestconfDocumentedException(
+                        "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
+                        "\" for POST operation.",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
             final String completeIdentifier = this.addMountPointIdentifier(identifier);
@@ -687,8 +641,9 @@ public class RestconfImpl implements RestconfService {
             MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
             final Module module = this.findModule(mountPoint, payload);
             if (module == null) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                                            "Module was not found for \"" + payloadNS + "\"");
+                throw new RestconfDocumentedException(
+                        "Module was not found for \"" + payloadNS + "\"",
+                        ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
             }
 
             String payloadName = this.getName(payload);
@@ -714,9 +669,8 @@ public class RestconfImpl implements RestconfService {
                 status = future == null ? null : future.get();
             }
         }
-        catch( ResponseException e ){ throw e; }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error creating data" );
+            throw new RestconfDocumentedException( "Error creating data", e );
         }
 
         if (status == null) {
@@ -733,14 +687,16 @@ public class RestconfImpl implements RestconfService {
     public Response createConfigurationData(final CompositeNode payload) {
         URI payloadNS = this.namespace(payload);
         if (payloadNS == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
+            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 );
         }
 
         final Module module = this.findModule(null, payload);
         if (module == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                    "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
+            throw new RestconfDocumentedException(
+                    "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
+                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
         }
 
         String payloadName = this.getName(payload);
@@ -764,9 +720,8 @@ public class RestconfImpl implements RestconfService {
                 status = future == null ? null : future.get();
             }
         }
-       catch( ResponseException e ){ throw e; }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error creating data" );
+            throw new RestconfDocumentedException( "Error creating data", e );
         }
 
         if (status == null) {
@@ -795,7 +750,7 @@ public class RestconfImpl implements RestconfService {
             }
         }
         catch( Exception e ) {
-            throw new ResponseException( e, "Error creating data" );
+            throw new RestconfDocumentedException( "Error creating data", e );
         }
 
         if( status.getResult() == TransactionStatus.COMMITED )
@@ -808,12 +763,14 @@ public class RestconfImpl implements RestconfService {
     public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
         final String streamName = Notificator.createStreamNameFromUri(identifier);
         if (Strings.isNullOrEmpty(streamName)) {
-            throw new ResponseException(Status.BAD_REQUEST, "Stream name is empty.");
+            throw new RestconfDocumentedException(
+                    "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         final ListenerAdapter listener = Notificator.getListenerFor(streamName);
         if (listener == null) {
-            throw new ResponseException(Status.BAD_REQUEST, "Stream was not found.");
+            throw new RestconfDocumentedException(
+                    "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
         }
 
         broker.registerToListenDataChanges(listener);
@@ -917,9 +874,10 @@ public class RestconfImpl implements RestconfService {
             }
 
             if (dataNodeKeyValueObject == null) {
-                throw new ResponseException(Status.BAD_REQUEST,
-                            "Data contains list \"" + dataNode.getNodeType().getLocalName() +
-                            "\" which does not contain key: \"" + key.getLocalName() + "\"");
+                throw new RestconfDocumentedException(
+                        "Data contains list \"" + dataNode.getNodeType().getLocalName() +
+                        "\" which does not contain key: \"" + key.getLocalName() + "\"",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
             keyValues.put(key, dataNodeKeyValueObject);
@@ -954,14 +912,16 @@ public class RestconfImpl implements RestconfService {
         if (schema == null) {
             QName nodeType = node == null ? null : node.getNodeType();
             String localName = nodeType == null ? null : nodeType.getLocalName();
-            String _plus = ("Data schema node was not found for " + localName);
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
-                                        "Data schema node was not found for " + localName );
+
+            throw new RestconfDocumentedException(
+                    "Data schema node was not found for " + localName,
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         if (!(schema instanceof DataNodeContainer)) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                                        "Root element has to be container or list yang datatype.");
+            throw new RestconfDocumentedException(
+                    "Root element has to be container or list yang datatype.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         if ((node instanceof CompositeNodeWrapper)) {
@@ -970,8 +930,9 @@ public class RestconfImpl implements RestconfService {
                 try {
                     this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
                 }
-                catch (NumberFormatException e) {
-                    throw new ResponseException(Status.BAD_REQUEST, e.getMessage());
+                catch (IllegalArgumentException e) {
+                    throw new RestconfDocumentedException(
+                                    e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                 }
             }
 
@@ -985,9 +946,10 @@ public class RestconfImpl implements RestconfService {
                                final DataSchemaNode schema, final QName previousAugment,
                                final MountInstance mountPoint) {
         if (schema == null) {
-            throw new ResponseException(Status.BAD_REQUEST,
-                                        "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
-                                        "\" does not exist in yang schema.");
+            throw new RestconfDocumentedException(
+                    "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
+                    "\" does not exist in yang schema.",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
         }
 
         QName currentAugment = null;
@@ -997,13 +959,14 @@ public class RestconfImpl implements RestconfService {
         else {
             currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
             if (nodeBuilder.getQname() == null) {
-                throw new ResponseException(Status.BAD_REQUEST,
+                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() + "\".");
+                        schema.getQName().getNamespace() + "\".",
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
         }
 
@@ -1021,11 +984,12 @@ public class RestconfImpl implements RestconfService {
                                .append("\n");
                     }
 
-                    throw new ResponseException(Status.BAD_REQUEST,
+                    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);
+                                 "\nThe node is added as augment from modules with namespaces:\n" + builder,
+                                 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                 }
 
                 boolean rightNodeSchemaFound = false;
@@ -1041,8 +1005,9 @@ public class RestconfImpl implements RestconfService {
                 }
 
                 if (!rightNodeSchemaFound) {
-                    throw new ResponseException(Status.BAD_REQUEST,
-                                      "Schema node \"" + child.getLocalName() + "\" was not found in module.");
+                    throw new RestconfDocumentedException(
+                               "Schema node \"" + child.getLocalName() + "\" was not found in module.",
+                               ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
                 }
             }
 
@@ -1057,9 +1022,10 @@ public class RestconfImpl implements RestconfService {
                     }
 
                     if (!foundKey) {
-                        throw new ResponseException(Status.BAD_REQUEST,
+                        throw new RestconfDocumentedException(
                                        "Missing key in URI \"" + listKey.getLocalName() +
-                                       "\" of list \"" + schema.getQName().getLocalName() + "\"");
+                                       "\" of list \"" + schema.getQName().getLocalName() + "\"",
+                                       ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
                     }
                 }
             }
index 446d07d42639969eec1f999fb8713b96d0a3f307..0bc8428d76f85fae184b577040ee4d52f9cc682d 100644 (file)
@@ -7,6 +7,15 @@
 */
 package org.opendaylight.controller.sal.restconf.rpc.impl;
 
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 
 public abstract class AbstractRpcExecutor implements RpcExecutor {
@@ -20,4 +29,41 @@ public abstract class AbstractRpcExecutor implements RpcExecutor {
     public RpcDefinition getRpcDefinition() {
         return rpcDef;
     }
+
+    protected RpcResult<CompositeNode> getRpcResult(
+                                            Future<RpcResult<CompositeNode>> fromFuture ) {
+        try {
+            return fromFuture.get();
+        }
+        catch( InterruptedException e ) {
+            throw new RestconfDocumentedException(
+                        "The operation was interrupted while executing and did not complete.",
+                        ErrorType.RPC, ErrorTag.PARTIAL_OPERATION );
+        }
+        catch( ExecutionException e ) {
+            Throwable cause = e.getCause();
+            if( cause instanceof CancellationException ) {
+                throw new RestconfDocumentedException(
+                        "The operation was cancelled while executing.",
+                        ErrorType.RPC, ErrorTag.PARTIAL_OPERATION );
+            }
+            else if( cause != null ){
+                while( cause.getCause() != null ) {
+                    cause = cause.getCause();
+                }
+
+                if( cause instanceof IllegalArgumentException ) {
+                    throw new RestconfDocumentedException(
+                            cause.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+                }
+
+                throw new RestconfDocumentedException(
+                       "The operation encountered an unexpected error while executing.", cause );
+            }
+            else {
+                throw new RestconfDocumentedException(
+                        "The operation encountered an unexpected error while executing.", e );
+            }
+        }
+    }
 }
\ No newline at end of file
index 0748832247defac742db103e0268edcce56bb90c..249b657d496218b40c4893c22d9f16ebc48f1b92 100644 (file)
@@ -23,6 +23,6 @@ public class BrokerRpcExecutor extends AbstractRpcExecutor {
 
     @Override
     public RpcResult<CompositeNode> invokeRpc(CompositeNode rpcRequest) {
-        return broker.invokeRpc( getRpcDefinition().getQName(), rpcRequest );
+        return getRpcResult( broker.invokeRpc( getRpcDefinition().getQName(), rpcRequest ) );
     }
 }
\ No newline at end of file
index b56db21951ee2d2eb01f6e3208ebb8912d0a5530..da19a0034de26103afd793e0ebb4a5d002f3bffa 100644 (file)
@@ -7,18 +7,13 @@
 */
 package org.opendaylight.controller.sal.restconf.rpc.impl;
 
-import java.util.concurrent.ExecutionException;
-
-import javax.ws.rs.core.Response.Status;
-
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.ListenableFuture;
 
 /**
  * Provides an implementation which invokes rpc methods via a mounted yang data model.
@@ -35,14 +30,8 @@ public class MountPointRpcExecutor extends AbstractRpcExecutor {
     }
 
     @Override
-    public RpcResult<CompositeNode> invokeRpc( CompositeNode rpcRequest ) throws ResponseException {
-        ListenableFuture<RpcResult<CompositeNode>> rpcFuture =
-                mountPoint.rpc( getRpcDefinition().getQName(), rpcRequest);
-        try {
-            return rpcFuture.get();
-        } catch (InterruptedException | ExecutionException e) {
-            throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
-                                        e.getCause().getMessage() );
-        }
+    public RpcResult<CompositeNode> invokeRpc( CompositeNode rpcRequest )
+                                                   throws RestconfDocumentedException {
+        return getRpcResult( mountPoint.rpc( getRpcDefinition().getQName(), rpcRequest ) );
     }
 }
\ No newline at end of file
index 415d58e53d3f43684aaca2edfde78eee91a033ca..3c70cca0f87806d28b73273423fa7b546b1631c6 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.controller.sal.restconf.impl.json.to.cnsn.test;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -16,13 +15,11 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-import javax.ws.rs.WebApplicationException;
-
 import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -32,8 +29,6 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.gson.JsonSyntaxException;
-
 public class JsonToCnSnTest {
 
     private static final Logger LOG = LoggerFactory.getLogger(JsonToCnSnTest.class);
@@ -116,44 +111,39 @@ public class JsonToCnSnTest {
 
     @Test
     public void incorrectTopLevelElementsTest() {
-        Throwable cause1 = null;
+        RestconfDocumentedException cause1 = null;
         try {
-            TestUtils
-            .readInputToCnSn("/json-to-cnsn/wrong-top-level1.json", true, JsonToCompositeNodeProvider.INSTANCE);
-        } catch (WebApplicationException e) {
+            TestUtils.readInputToCnSn("/json-to-cnsn/wrong-top-level1.json", true, JsonToCompositeNodeProvider.INSTANCE);
+        } catch (RestconfDocumentedException e) {
             cause1 = e;
         }
 
         assertNotNull(cause1);
-        assertTrue(cause1
-                .getCause()
-                .getMessage()
-                .contains(
-                        "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."));
+        assertTrue(cause1.getErrors().get( 0 ).getErrorMessage().contains(
+            "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."));
 
-        Throwable cause2 = null;
+        RestconfDocumentedException cause2 = null;
         try {
             TestUtils
-            .readInputToCnSn("/json-to-cnsn/wrong-top-level2.json", true, JsonToCompositeNodeProvider.INSTANCE);
-        } catch (WebApplicationException e) {
+                    .readInputToCnSn("/json-to-cnsn/wrong-top-level2.json", true, JsonToCompositeNodeProvider.INSTANCE);
+        } catch (RestconfDocumentedException e) {
             cause2 = e;
         }
         assertNotNull(cause2);
-        assertTrue(cause2.getCause().getMessage().contains("Json Object should contain one element"));
+        assertTrue(cause2.getErrors().get( 0 ).getErrorMessage().contains(
+                                                     "Json Object should contain one element"));
 
-        Throwable cause3 = null;
+        RestconfDocumentedException cause3 = null;
         try {
             TestUtils
-            .readInputToCnSn("/json-to-cnsn/wrong-top-level3.json", true, JsonToCompositeNodeProvider.INSTANCE);
-        } catch (WebApplicationException e) {
+
+                    .readInputToCnSn("/json-to-cnsn/wrong-top-level3.json", true, JsonToCompositeNodeProvider.INSTANCE);
+        } catch (RestconfDocumentedException e) {
             cause3 = e;
         }
         assertNotNull(cause3);
-        assertTrue(cause3
-                .getCause()
-                .getMessage()
-                .contains(
-                        "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."));
+        assertTrue(cause3.getErrors().get( 0 ).getErrorMessage().contains(
+            "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet."));
 
     }
 
@@ -178,8 +168,8 @@ public class JsonToCnSnTest {
         String reason = null;
         try {
             TestUtils.readInputToCnSn("/json-to-cnsn/empty-data1.json", true, JsonToCompositeNodeProvider.INSTANCE);
-        } catch (JsonSyntaxException e) {
-            reason = e.getMessage();
+        } catch (RestconfDocumentedException e) {
+            reason = e.getErrors().get( 0 ).getErrorMessage();
         }
 
         assertTrue(reason.contains("Expected value at line"));
@@ -278,15 +268,9 @@ public class JsonToCnSnTest {
     @Ignore
     @Test
     public void loadDataAugmentedSchemaMoreEqualNamesTest() {
-        boolean exceptionCaught = false;
-        try {
-            loadAndNormalizeData("/common/augment/json/dataa.json", "/common/augment/yang", "cont", "main");
-            loadAndNormalizeData("/common/augment/json/datab.json", "/common/augment/yang", "cont", "main");
-        } catch (ResponseException e) {
-            exceptionCaught = true;
-        }
+        loadAndNormalizeData("/common/augment/json/dataa.json", "/common/augment/yang", "cont", "main");
+        loadAndNormalizeData("/common/augment/json/datab.json", "/common/augment/yang", "cont", "main");
 
-        assertFalse(exceptionCaught);
     }
 
     private void simpleTest(final String jsonPath, final String yangPath, final String topLevelElementName, final String namespace,
@@ -399,8 +383,8 @@ public class JsonToCnSnTest {
         try {
             TestUtils.readInputToCnSn("/json-to-cnsn/unsupported-json-format.json", true,
                     JsonToCompositeNodeProvider.INSTANCE);
-        } catch (WebApplicationException e) {
-            exceptionMessage = e.getCause().getMessage();
+        } catch (RestconfDocumentedException e) {
+            exceptionMessage = e.getErrors().get( 0 ).getErrorMessage();
         }
         assertTrue(exceptionMessage.contains("Root element of Json has to be Object"));
     }
index 18199de8c63ca056ed8aacbf947fe0d36f2afa0d..ddab7004408f4d60aa3e89f8660f8aa9749bce7b 100644 (file)
@@ -9,6 +9,7 @@
 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.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
@@ -19,8 +20,6 @@ import static org.mockito.Mockito.when;
 import java.util.Map;
 import java.util.concurrent.Future;
 
-import javax.ws.rs.core.Response.Status;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InOrder;
@@ -34,7 +33,8 @@ import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError;
 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.controller.sal.streams.listeners.Notificator;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -120,7 +120,7 @@ public class BrokerFacadeTest {
         assertSame( "readOperationalDataBehindMountPoint", dataNode, actualNode );
     }
 
-    @Test(expected=ResponseException.class)
+    @Test(expected=RestconfDocumentedException.class)
     public void testReadOperationalDataWithNoDataBroker() {
         brokerFacade.setDataService( null );
 
@@ -129,26 +129,19 @@ public class BrokerFacadeTest {
 
     @SuppressWarnings("unchecked")
     @Test
-    public void testInvokeRpc() {
+    public void testInvokeRpc() throws Exception {
         RpcResult<CompositeNode> expResult = mock( RpcResult.class );
         Future<RpcResult<CompositeNode>> future = Futures.immediateFuture( expResult );
         when( mockConsumerSession.rpc( qname, dataNode ) ).thenReturn( future );
 
-        RpcResult<CompositeNode> actualResult = brokerFacade.invokeRpc( qname, dataNode );
+        Future<RpcResult<CompositeNode>> actualFuture = brokerFacade.invokeRpc( qname, dataNode );
+        assertNotNull( "Future is null", actualFuture );
+        RpcResult<CompositeNode> actualResult = actualFuture.get();
 
         assertSame( "invokeRpc", expResult, actualResult );
     }
 
-    @Test(expected=ResponseException.class)
-    public void testInvokeRpcWithException() {
-        Exception mockEx = new Exception( "mock" );
-        Future<RpcResult<CompositeNode>> future = Futures.immediateFailedFuture( mockEx );
-        when( mockConsumerSession.rpc( qname, dataNode ) ).thenReturn( future );
-
-        brokerFacade.invokeRpc( qname, dataNode );
-    }
-
-    @Test(expected=ResponseException.class)
+    @Test(expected=RestconfDocumentedException.class)
     public void testInvokeRpcWithNoConsumerSession() {
         brokerFacade.setContext( null );
 
@@ -218,7 +211,7 @@ public class BrokerFacadeTest {
         inOrder.verify( mockTransaction ).commit();
     }
 
-    @Test(expected=ResponseException.class)
+    @Test(expected=RestconfDocumentedException.class)
     public void testCommitConfigurationDataPostAlreadyExists() {
         when( dataBroker.beginTransaction() ).thenReturn( mockTransaction );
         mockTransaction.putConfigurationData( instanceID, dataNode );
@@ -226,10 +219,10 @@ public class BrokerFacadeTest {
             .thenReturn( dataNode );
         try {
             brokerFacade.commitConfigurationDataPost( instanceID, dataNode );
-        } catch (ResponseException e) {
-            assertEquals("Unexpect Exception Status -> "
-                    + "http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48",
-                    (e.getResponse().getStatus()), Status.CONFLICT.getStatusCode());
+        }
+        catch (RestconfDocumentedException e) {
+            assertEquals("getErrorTag",
+                    RestconfError.ErrorTag.DATA_EXISTS, e.getErrors().get( 0 ).getErrorTag());
             throw e;
         }
     }
@@ -259,7 +252,7 @@ public class BrokerFacadeTest {
         inOrder.verify( mockTransaction ).commit();
     }
 
-    @Test(expected=ResponseException.class)
+    @Test(expected=RestconfDocumentedException.class)
     public void testCommitConfigurationDataPostBehindMountPointAlreadyExists() {
 
         when( mockMountInstance.beginTransaction() ).thenReturn( mockTransaction );
@@ -269,10 +262,10 @@ public class BrokerFacadeTest {
         try {
             brokerFacade.commitConfigurationDataPostBehindMountPoint( mockMountInstance,
                     instanceID, dataNode );
-        } catch (ResponseException e) {
-            assertEquals("Unexpect Exception Status -> "
-                    + "http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48",
-                    e.getResponse().getStatus(), Status.CONFLICT.getStatusCode());
+        }
+        catch (RestconfDocumentedException e) {
+            assertEquals("getErrorTag",
+                    RestconfError.ErrorTag.DATA_EXISTS, e.getErrors().get( 0 ).getErrorTag());
             throw e;
         }
     }
index 767aaf36c149a9a61763d71f9e7722f997e9a595..51687e2a1285601e4efff6c3737001a9f12488f3 100644 (file)
@@ -14,6 +14,7 @@ import org.glassfish.jersey.test.JerseyTest;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
@@ -46,6 +47,7 @@ public class CodecsExceptionsCatchingTest extends JerseyTest {
         resourceConfig = resourceConfig.registerInstances(restConf, StructuredDataToXmlProvider.INSTANCE,
                 StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
                 JsonToCompositeNodeProvider.INSTANCE);
+        resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
         return resourceConfig;
     }
 
index 018a235718323b9c783bbbf0612c96f484de61a1..c0c86c3f25384fdbd3ea979debef62fa7f1512ab 100644 (file)
@@ -22,28 +22,30 @@ import static org.mockito.Mockito.when;
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
-import javax.ws.rs.core.Response.Status;
-
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
+import org.opendaylight.controller.sal.common.util.Rpcs;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
@@ -54,6 +56,8 @@ 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 com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 
 public class InvokeRpcMethodTest {
@@ -61,13 +65,6 @@ public class InvokeRpcMethodTest {
     private RestconfImpl restconfImpl = null;
     private static ControllerContext controllerContext = null;
 
-    private class AnswerImpl implements Answer<RpcResult<CompositeNode>> {
-        @Override
-        public RpcResult<CompositeNode> answer(final InvocationOnMock invocation) throws Throwable {
-            CompositeNode compNode = (CompositeNode) invocation.getArguments()[1];
-            return new DummyRpcResult.Builder<CompositeNode>().result(compNode).isSuccessful(true).build();
-        }
-    }
 
     @BeforeClass
     public static void init() throws FileNotFoundException {
@@ -111,9 +108,13 @@ public class InvokeRpcMethodTest {
         restconf.setBroker(mockedBrokerFacade);
         restconf.setControllerContext(contContext);
 
-        when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenAnswer(new AnswerImpl());
+        CompositeNode payload = preparePayload();
+
+        when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class)))
+            .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture(
+                                               Rpcs.<CompositeNode>getRpcResult( true ) ) );
 
-        StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", preparePayload());
+        StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload);
         assertTrue(structData == null);
 
     }
@@ -131,74 +132,90 @@ public class InvokeRpcMethodTest {
 
     @Test
     public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
-        RpcResult<CompositeNode> rpcResult = mock(RpcResult.class);
-        when(rpcResult.isSuccessful()).thenReturn(false);
+        RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( false );
 
-        ArgumentCaptor<CompositeNode> payload = ArgumentCaptor
-                .forClass(CompositeNode.class);
         BrokerFacade brokerFacade = mock(BrokerFacade.class);
-        when(
-                brokerFacade.invokeRpc(
-                        eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
-                        payload.capture())).thenReturn(rpcResult);
+        when( brokerFacade.invokeRpc(
+                 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
+                 any(CompositeNode.class)))
+            .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
 
         restconfImpl.setBroker(brokerFacade);
 
         try {
             restconfImpl.invokeRpc("toaster:cancel-toast", "");
             fail("Expected an exception to be thrown.");
-        } catch (ResponseException e) {
-            assertEquals(e.getMessage(),
-                    Status.INTERNAL_SERVER_ERROR.getStatusCode(), e
-                    .getResponse().getStatus());
+        }
+        catch (RestconfDocumentedException e) {
+            verifyRestconfDocumentedException( e, 0, ErrorType.RPC, ErrorTag.OPERATION_FAILED,
+                                               Optional.<String>absent(), Optional.<String>absent() );
         }
     }
 
-    @Test
-    public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
-        List<RpcError> rpcErrors = new LinkedList<RpcError>();
+    void verifyRestconfDocumentedException( final RestconfDocumentedException e, final int index,
+                                            final ErrorType expErrorType, final ErrorTag expErrorTag,
+                                            final Optional<String> expErrorMsg,
+                                            final Optional<String> expAppTag ) {
+        RestconfError actual = null;
+        try {
+            actual = e.getErrors().get( index );
+        }
+        catch( ArrayIndexOutOfBoundsException ex ) {
+            fail( "RestconfError not found at index " + index );
+        }
+
+        assertEquals( "getErrorType", expErrorType, actual.getErrorType() );
+        assertEquals( "getErrorTag", expErrorTag, actual.getErrorTag() );
+        assertNotNull( "getErrorMessage is null", actual.getErrorMessage() );
+
+        if( expErrorMsg.isPresent() ) {
+            assertEquals( "getErrorMessage", expErrorMsg.get(), actual.getErrorMessage() );
+        }
 
-        RpcError unknownError = mock(RpcError.class);
-        when( unknownError.getTag() ).thenReturn( "bogusTag" );
-        rpcErrors.add( unknownError );
+        if( expAppTag.isPresent() ) {
+            assertEquals( "getErrorAppTag", expAppTag.get(), actual.getErrorAppTag() );
+        }
+    }
 
-        RpcError knownError = mock( RpcError.class );
-        when( knownError.getTag() ).thenReturn( "in-use" );
-        rpcErrors.add( knownError );
+    @Test
+    public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
+        List<RpcError> rpcErrors = Arrays.asList(
+            RpcErrors.getRpcError( null, "bogusTag", null, ErrorSeverity.ERROR, "foo",
+                                   RpcError.ErrorType.TRANSPORT, null ),
+            RpcErrors.getRpcError( "app-tag", "in-use", null, ErrorSeverity.WARNING, "bar",
+                                   RpcError.ErrorType.RPC, null ));
 
-        RpcResult<CompositeNode> rpcResult = mock(RpcResult.class);
-        when(rpcResult.isSuccessful()).thenReturn(false);
-        when(rpcResult.getErrors()).thenReturn( rpcErrors  );
+        RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( false, rpcErrors );
 
-        ArgumentCaptor<CompositeNode> payload = ArgumentCaptor
-                .forClass(CompositeNode.class);
         BrokerFacade brokerFacade = mock(BrokerFacade.class);
-        when(
-                brokerFacade.invokeRpc(
-                        eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
-                        payload.capture())).thenReturn(rpcResult);
+        when( brokerFacade.invokeRpc(
+                 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
+                 any(CompositeNode.class)))
+            .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
 
         restconfImpl.setBroker(brokerFacade);
 
         try {
             restconfImpl.invokeRpc("toaster:cancel-toast", "");
             fail("Expected an exception to be thrown.");
-        } catch (ResponseException e) {
-            //TODO: Change to a 409 in the future - waiting on additional BUG to enhance this.
-            assertEquals(e.getMessage(), 500, e.getResponse().getStatus());
+        }
+        catch (RestconfDocumentedException e) {
+            verifyRestconfDocumentedException( e, 0, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED,
+                                               Optional.of( "foo" ), Optional.<String>absent() );
+            verifyRestconfDocumentedException( e, 1, ErrorType.RPC, ErrorTag.IN_USE,
+                                               Optional.of( "bar" ), Optional.of( "app-tag" ) );
         }
     }
 
     @Test
     public void testInvokeRpcWithNoPayload_Success() {
-        RpcResult<CompositeNode> rpcResult = mock(RpcResult.class);
-        when(rpcResult.isSuccessful()).thenReturn(true);
+        RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
 
         BrokerFacade brokerFacade = mock(BrokerFacade.class);
-        when(
-                brokerFacade.invokeRpc(
-                        eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
-                        any( CompositeNode.class ))).thenReturn(rpcResult);
+        when( brokerFacade.invokeRpc(
+                 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast")),
+                 any( CompositeNode.class )))
+            .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
 
         restconfImpl.setBroker(brokerFacade);
 
@@ -213,10 +230,9 @@ public class InvokeRpcMethodTest {
         try {
             restconfImpl.invokeRpc("toaster:cancel-toast", " a payload ");
             fail("Expected an exception");
-        } catch (ResponseException e) {
-            assertEquals(e.getMessage(),
-                    Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode(), e
-                    .getResponse().getStatus());
+        } catch (RestconfDocumentedException e) {
+            verifyRestconfDocumentedException( e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                                               Optional.<String>absent(), Optional.<String>absent() );
         }
     }
 
@@ -225,24 +241,24 @@ public class InvokeRpcMethodTest {
         try {
             restconfImpl.invokeRpc("toaster:bad-method", "");
             fail("Expected an exception");
-        } catch (ResponseException e) {
-            assertEquals(e.getMessage(), Status.NOT_FOUND.getStatusCode(), e
-                    .getResponse().getStatus());
+        }
+        catch (RestconfDocumentedException e) {
+            verifyRestconfDocumentedException( e, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT,
+                                               Optional.<String>absent(), Optional.<String>absent() );
         }
     }
 
     @Test
     public void testInvokeRpcMethodWithInput() {
-        RpcResult<CompositeNode> rpcResult = mock(RpcResult.class);
-        when(rpcResult.isSuccessful()).thenReturn(true);
+        RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
 
         CompositeNode payload = mock(CompositeNode.class);
 
         BrokerFacade brokerFacade = mock(BrokerFacade.class);
-        when(
-                brokerFacade.invokeRpc(
-                        eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast")),
-                        any(CompositeNode.class))).thenReturn(rpcResult);
+        when( brokerFacade.invokeRpc(
+                 eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast")),
+                 any(CompositeNode.class)))
+            .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
 
         restconfImpl.setBroker(brokerFacade);
 
@@ -257,29 +273,28 @@ public class InvokeRpcMethodTest {
         try {
             restconfImpl.invokeRpc("toaster/slash", "");
             fail("Expected an exception.");
-        } catch (ResponseException e) {
-            assertEquals(e.getMessage(), Status.NOT_FOUND.getStatusCode(), e
-                    .getResponse().getStatus());
+        }
+        catch (RestconfDocumentedException e) {
+            verifyRestconfDocumentedException( e, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                                               Optional.<String>absent(), Optional.<String>absent() );
         }
     }
 
     @Test
     public void testInvokeRpcWithNoPayloadWithOutput_Success() {
-        RpcResult<CompositeNode> rpcResult = mock(RpcResult.class);
-        when(rpcResult.isSuccessful()).thenReturn(true);
-
         CompositeNode compositeNode = mock( CompositeNode.class );
-        when( rpcResult.getResult() ).thenReturn( compositeNode );
+        RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true, compositeNode,
+                                                            Collections.<RpcError>emptyList() );
 
         BrokerFacade brokerFacade = mock(BrokerFacade.class);
         when( brokerFacade.invokeRpc(
-                eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)testOutput")),
-                any( CompositeNode.class ))).thenReturn(rpcResult);
+                        eq(QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)testOutput")),
+                        any( CompositeNode.class )))
+            .thenReturn( Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ) );
 
         restconfImpl.setBroker(brokerFacade);
 
-        StructuredData output = restconfImpl.invokeRpc("toaster:testOutput",
-                "");
+        StructuredData output = restconfImpl.invokeRpc("toaster:testOutput", "");
         assertNotNull( output );
         assertSame( compositeNode, output.getData() );
         assertNotNull( output.getSchema() );
@@ -288,8 +303,7 @@ public class InvokeRpcMethodTest {
     @Test
     public void testMountedRpcCallNoPayload_Success() throws Exception
     {
-        RpcResult<CompositeNode> rpcResult = mock(RpcResult.class);
-        when(rpcResult.isSuccessful()).thenReturn(true);
+        RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode>getRpcResult( true );
 
         ListenableFuture<RpcResult<CompositeNode>> mockListener = mock( ListenableFuture.class );
         when( mockListener.get() ).thenReturn( rpcResult );
@@ -321,6 +335,4 @@ public class InvokeRpcMethodTest {
 
         //additional validation in the fact that the restconfImpl does not throw an exception.
     }
-
-
 }
index 6d2723c2f17391e8fff4d30e983d236aa5e39c10..f5aa453fa245d2e4f9273350f35dcb1c47cfbd47 100644 (file)
@@ -15,7 +15,7 @@ import java.net.URISyntaxException;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
@@ -26,48 +26,28 @@ public class NormalizeNodeTest extends YangAndXmlAndDataSchemaLoader {
         dataLoad("/normalize-node/yang/");
     }
 
-    @Test
+    @Test(expected=RestconfDocumentedException.class)
     public void namespaceNotNullAndInvalidNamespaceAndNoModuleNameTest() {
-        boolean exceptionReised = false;
-        try {
-            TestUtils.normalizeCompositeNode(prepareCnSn("wrongnamespace"), modules, schemaNodePath);
-        } catch (ResponseException e) {
-            exceptionReised = true;
-        }
-        assertTrue(exceptionReised);
+
+        TestUtils.normalizeCompositeNode(prepareCnSn("wrongnamespace"), modules, schemaNodePath);
     }
 
     @Test
     public void namespaceNullTest() {
-        String exceptionMessage = null;
-        try {
-            TestUtils.normalizeCompositeNode(prepareCnSn(null), modules, schemaNodePath);
-        } catch (ResponseException e) {
-            exceptionMessage = String.valueOf(e.getResponse().getEntity());
-        }
-        assertNull(exceptionMessage);
+
+        TestUtils.normalizeCompositeNode(prepareCnSn(null), modules, schemaNodePath);
     }
 
     @Test
     public void namespaceValidNamespaceTest() {
-        String exceptionMessage = null;
-        try {
-            TestUtils.normalizeCompositeNode(prepareCnSn("normalize:node:module"), modules, schemaNodePath);
-        } catch (ResponseException e) {
-            exceptionMessage = String.valueOf(e.getResponse().getEntity());
-        }
-        assertNull(exceptionMessage);
+
+        TestUtils.normalizeCompositeNode(prepareCnSn("normalize:node:module"), modules, schemaNodePath);
     }
 
     @Test
     public void namespaceValidModuleNameTest() {
-        String exceptionMessage = null;
-        try {
-            TestUtils.normalizeCompositeNode(prepareCnSn("normalize-node-module"), modules, schemaNodePath);
-        } catch (ResponseException e) {
-            exceptionMessage = String.valueOf(e.getResponse().getEntity());
-        }
-        assertNull(exceptionMessage);
+
+        TestUtils.normalizeCompositeNode(prepareCnSn("normalize-node-module"), modules, schemaNodePath);
     }
 
     private CompositeNode prepareCnSn(String namespace) {
index a6391894c2f044dd13d98518db473c952e44d1e8..53183c611cf54329d5555b2de8b56b01cedfdf0d 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.controller.sal.restconf.impl.test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.FileNotFoundException;
 
@@ -18,16 +19,16 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class RestGetAugmentedElementWhenEqualNamesTest {
-    
+
     private static ControllerContext controllerContext = ControllerContext.getInstance();
-    
+
     @Rule
     public ExpectedException exception = ExpectedException.none();
-    
+
     @BeforeClass
     public static void init() throws FileNotFoundException {
         SchemaContext schemaContextTestModule = TestUtils.loadSchemaContext("/common/augment/yang");
@@ -41,17 +42,15 @@ public class RestGetAugmentedElementWhenEqualNamesTest {
         iiWithData = controllerContext.toInstanceIdentifier("main:cont/augment-main-b:cont1");
         assertEquals("ns:augment:main:b", iiWithData.getSchemaNode().getQName().getNamespace().toString());
     }
-    
+
     @Test
     public void nodeWithoutNamespaceHasMoreAugments() {
-        boolean exceptionCaught = false;
         try {
             controllerContext.toInstanceIdentifier("main:cont/cont1");
-        } catch (ResponseException e) {
-            assertTrue(((String) e.getResponse().getEntity()).contains("is added as augment from more than one module"));
-            exceptionCaught = true;
+            fail( "Expected exception" );
+        } catch (RestconfDocumentedException e) {
+            assertTrue(e.getErrors().get( 0 ).getErrorMessage().contains(
+                                    "is added as augment from more than one module"));
         }
-        assertTrue(exceptionCaught);
     }
-
 }
index 4198e20b838e15391c650f5ffd34afdcc5693840..893622f60a18ae8a0b8e5e394f9631d0f418374c 100644 (file)
@@ -34,11 +34,11 @@ import javax.ws.rs.core.Response;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.core.api.mount.MountService;
 import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
@@ -92,6 +92,7 @@ public class RestGetOperationTest extends JerseyTest {
         resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
                 StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
                 JsonToCompositeNodeProvider.INSTANCE);
+        resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
         return resourceConfig;
     }
 
@@ -145,15 +146,15 @@ public class RestGetOperationTest extends JerseyTest {
 
     /**
      * MountPoint test. URI represents mount point.
-     * 
+     *
      * Slashes in URI behind mount point. lst1 element with key
      * GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is requested via
      * GET HTTP operation. It is tested whether %2F character is replaced with
      * simple / in InstanceIdentifier parameter in method
      * {@link BrokerFacade#readConfigurationDataBehindMountPoint(MountInstance, InstanceIdentifier)}
      * which is called in method {@link RestconfImpl#readConfigurationData}
-     * 
-     * 
+     *
+     *
      * @throws ParseException
      */
     @Test
index c6e2f1434371ed9fbd4e294ed7661d2acb5d3c38..ce460fe4746d9137f0775f6536297e5cc841ab44 100644 (file)
@@ -38,6 +38,7 @@ import org.opendaylight.controller.sal.core.api.mount.MountInstance;
 import org.opendaylight.controller.sal.core.api.mount.MountService;
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
 import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
@@ -52,6 +53,8 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+import com.google.common.util.concurrent.Futures;
+
 public class RestPostOperationTest extends JerseyTest {
 
     private static String xmlDataAbsolutePath;
@@ -99,6 +102,7 @@ public class RestPostOperationTest extends JerseyTest {
         resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
                 StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
                 JsonToCompositeNodeProvider.INSTANCE);
+        resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
         return resourceConfig;
     }
 
@@ -116,7 +120,7 @@ public class RestPostOperationTest extends JerseyTest {
         assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput));
 
         uri = "/operations/test-module:rpc-wrongtest";
-        assertEquals(404, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput));
+        assertEquals(400, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput));
     }
 
     @Test
@@ -173,7 +177,8 @@ public class RestPostOperationTest extends JerseyTest {
     private void mockInvokeRpc(CompositeNode result, boolean sucessful) {
         RpcResult<CompositeNode> rpcResult = new DummyRpcResult.Builder<CompositeNode>().result(result)
                 .isSuccessful(sucessful).build();
-        when(brokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(rpcResult);
+        when(brokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class)))
+            .thenReturn(Futures.<RpcResult<CompositeNode>>immediateFuture( rpcResult ));
     }
 
     private void mockCommitConfigurationDataPostMethod(TransactionStatus statusName) {
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java
new file mode 100644 (file)
index 0000000..fc5d7be
--- /dev/null
@@ -0,0 +1,966 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathFactory;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.rest.api.Draft02;
+import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError;
+import org.opendaylight.controller.sal.restconf.impl.StructuredData;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+/**
+ * Unit tests for RestconfDocumentedExceptionMapper.
+ *
+ * @author Thomas Pantelis
+ */
+public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
+
+    interface ErrorInfoVerifier {
+        void verifyXML( Node errorInfoNode );
+        void verifyJson( JsonElement errorInfoElement );
+    }
+
+    static class ComplexErrorInfoVerifier implements ErrorInfoVerifier {
+
+        Map<String, String> expErrorInfo;
+
+        public ComplexErrorInfoVerifier( Map<String, String> expErrorInfo ) {
+            this.expErrorInfo = expErrorInfo;
+        }
+
+        @Override
+        public void verifyXML( Node errorInfoNode ) {
+
+            Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
+            NodeList childNodes = errorInfoNode.getChildNodes();
+            for( int i = 0; i < childNodes.getLength(); i++ ) {
+                Node child = childNodes.item( i );
+                if( child  instanceof Element ) {
+                    String expValue = mutableExpMap.remove( child.getNodeName() );
+                    assertNotNull( "Found unexpected \"error-info\" child node: " +
+                                   child.getNodeName(), expValue );
+                    assertEquals( "Text content for \"error-info\" child node " +
+                                   child.getNodeName(), expValue, child.getTextContent() );
+                }
+            }
+
+            if( !mutableExpMap.isEmpty() ) {
+                fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
+            }
+        }
+
+        @Override
+        public void verifyJson( JsonElement errorInfoElement ) {
+
+            assertTrue( "\"error-info\" Json element is not an Object",
+                    errorInfoElement.isJsonObject() );
+
+            Map<String, String> actualErrorInfo = Maps.newHashMap();
+            for( Entry<String, JsonElement> entry: errorInfoElement.getAsJsonObject().entrySet() ) {
+                String leafName = entry.getKey();
+                JsonElement leafElement = entry.getValue();
+                actualErrorInfo.put( leafName, leafElement.getAsString() );
+            }
+
+            Map<String, String> mutableExpMap = Maps.newHashMap( expErrorInfo );
+            for( Entry<String,String> actual: actualErrorInfo.entrySet() ) {
+                String expValue = mutableExpMap.remove( actual.getKey() );
+                assertNotNull( "Found unexpected \"error-info\" child node: " +
+                                actual.getKey(), expValue );
+                assertEquals( "Text content for \"error-info\" child node " +
+                              actual.getKey(), expValue, actual.getValue() );
+            }
+
+            if( !mutableExpMap.isEmpty() ) {
+                fail( "Missing \"error-info\" child nodes: " + mutableExpMap );
+            }
+        }
+    }
+
+    static class SimpleErrorInfoVerifier implements ErrorInfoVerifier {
+
+        String expTextContent;
+
+        public SimpleErrorInfoVerifier( String expErrorInfo ) {
+            this.expTextContent = expErrorInfo;
+        }
+
+        void verifyContent( String actualContent ) {
+            assertNotNull( "Actual \"error-info\" text content is null", actualContent );
+            assertTrue( "", actualContent.contains( expTextContent ) );
+        }
+
+        @Override
+        public void verifyXML( Node errorInfoNode ) {
+            verifyContent( errorInfoNode.getTextContent() );
+        }
+
+        @Override
+        public void verifyJson( JsonElement errorInfoElement ) {
+            verifyContent( errorInfoElement.getAsString() );
+        }
+    }
+
+    static RestconfService mockRestConf = mock( RestconfService.class );
+
+    static XPath XPATH = XPathFactory.newInstance().newXPath();
+    static XPathExpression ERROR_LIST;
+    static XPathExpression ERROR_TYPE;
+    static XPathExpression ERROR_TAG;
+    static XPathExpression ERROR_MESSAGE;
+    static XPathExpression ERROR_APP_TAG;
+    static XPathExpression ERROR_INFO;
+
+    @BeforeClass
+    public static void init() throws Exception {
+        ControllerContext.getInstance().setGlobalSchema( TestUtils.loadSchemaContext("/modules") );
+
+        NamespaceContext nsContext = new NamespaceContext() {
+            @Override
+            public Iterator getPrefixes( String namespaceURI ) {
+                return null;
+            }
+
+            @Override
+            public String getPrefix( String namespaceURI ) {
+                return null;
+            }
+
+            @Override
+            public String getNamespaceURI( String prefix ) {
+                return "ietf-restconf".equals( prefix ) ? Draft02.RestConfModule.NAMESPACE : null;
+            }
+        };
+
+        XPATH.setNamespaceContext( nsContext );
+        ERROR_LIST = XPATH.compile( "ietf-restconf:errors/ietf-restconf:error" );
+        ERROR_TYPE = XPATH.compile( "ietf-restconf:error-type" );
+        ERROR_TAG = XPATH.compile( "ietf-restconf:error-tag" );
+        ERROR_MESSAGE = XPATH.compile( "ietf-restconf:error-message" );
+        ERROR_APP_TAG = XPATH.compile( "ietf-restconf:error-app-tag" );
+        ERROR_INFO = XPATH.compile( "ietf-restconf:error-info" );
+    }
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        reset( mockRestConf );
+        super.setUp();
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig();
+        resourceConfig = resourceConfig.registerInstances( mockRestConf, StructuredDataToXmlProvider.INSTANCE,
+                                                           StructuredDataToJsonProvider.INSTANCE );
+        resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
+        return resourceConfig;
+    }
+
+    void stageMockEx( RestconfDocumentedException ex ) {
+        reset( mockRestConf );
+        when( mockRestConf.readOperationalData( any( String.class ) ) ).thenThrow( ex );
+    }
+
+    void testJsonResponse( RestconfDocumentedException ex, Status expStatus, ErrorType expErrorType,
+                           ErrorTag expErrorTag, String expErrorMessage, String expErrorAppTag,
+                           ErrorInfoVerifier errorInfoVerifier ) throws Exception {
+
+        stageMockEx( ex );
+
+        Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
+
+        InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, expStatus );
+
+        verifyJsonResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
+                                expErrorAppTag, errorInfoVerifier );
+    }
+
+    @Test
+    public void testToJsonResponseWithMessageOnly() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
+                          ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
+
+        // To test verification code
+//        String json =
+//            "{ errors: {" +
+//            "    error: [{" +
+//            "      error-tag : \"operation-failed\"" +
+//            "      ,error-type : \"application\"" +
+//            "      ,error-message : \"An error occurred\"" +
+//            "      ,error-info : {" +
+//            "        session-id: \"123\"" +
+//            "        ,address: \"1.2.3.4\"" +
+//            "      }" +
+//            "    }]" +
+//            "  }" +
+//            "}";
+//
+//        verifyJsonResponseBody( new java.io.StringBufferInputStream(json ), ErrorType.APPLICATION,
+//            ErrorTag.OPERATION_FAILED, "An error occurred", null,
+//            com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
+    }
+
+    @Test
+    public void testToJsonResponseWithInUseErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.IN_USE ),
+                          Status.CONFLICT, ErrorType.PROTOCOL,
+                          ErrorTag.IN_USE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithInvalidValueErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
+                                                           ErrorTag.INVALID_VALUE ),
+                          Status.BAD_REQUEST, ErrorType.RPC,
+                          ErrorTag.INVALID_VALUE, "mock error", null, null );
+
+    }
+
+    @Test
+    public void testToJsonResponseWithTooBigErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
+                                                           ErrorTag.TOO_BIG ),
+                          Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
+                          ErrorTag.TOO_BIG, "mock error", null, null );
+
+    }
+
+    @Test
+    public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.MISSING_ATTRIBUTE ),
+                          Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                          ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithBadAttributeErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.BAD_ATTRIBUTE ),
+                          Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                          ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
+    }
+    @Test
+    public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.UNKNOWN_ATTRIBUTE ),
+                          Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                          ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithBadElementErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.BAD_ELEMENT ),
+                          Status.BAD_REQUEST,
+                          ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithUnknownElementErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.UNKNOWN_ELEMENT ),
+                          Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                          ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.UNKNOWN_NAMESPACE ),
+                          Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                          ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.MALFORMED_MESSAGE ),
+                          Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                          ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.ACCESS_DENIED ),
+                          Status.FORBIDDEN, ErrorType.PROTOCOL,
+                          ErrorTag.ACCESS_DENIED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithLockDeniedErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.LOCK_DENIED ),
+                          Status.CONFLICT, ErrorType.PROTOCOL,
+                          ErrorTag.LOCK_DENIED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.RESOURCE_DENIED ),
+                          Status.CONFLICT, ErrorType.PROTOCOL,
+                          ErrorTag.RESOURCE_DENIED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.ROLLBACK_FAILED ),
+                          Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
+                          ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithDataExistsErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.DATA_EXISTS ),
+                          Status.CONFLICT, ErrorType.PROTOCOL,
+                          ErrorTag.DATA_EXISTS, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.DATA_MISSING ),
+                          Status.CONFLICT, ErrorType.PROTOCOL,
+                          ErrorTag.DATA_MISSING, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.OPERATION_NOT_SUPPORTED ),
+                          Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
+                          ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithOperationFailedErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.OPERATION_FAILED ),
+                          Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
+                          ErrorTag.OPERATION_FAILED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithPartialOperationErrorTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.PARTIAL_OPERATION ),
+                          Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
+                          ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithErrorAppTag() throws Exception {
+
+        testJsonResponse( new RestconfDocumentedException( new RestconfError(
+                                   ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+                                   "mock error", "mock-app-tag" ) ),
+                          Status.BAD_REQUEST, ErrorType.APPLICATION,
+                          ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
+    }
+
+    @Test
+    public void testToJsonResponseWithMultipleErrors() throws Exception {
+
+        List<RestconfError> errorList = Arrays.asList(
+                new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
+                new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
+        stageMockEx( new RestconfDocumentedException( errorList ) );
+
+        Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
+
+        InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON, Status.CONFLICT );
+
+        JsonArray arrayElement = parseJsonErrorArrayElement( stream );
+
+        assertEquals( "\"error\" Json array element length", 2, arrayElement.size() );
+
+        verifyJsonErrorNode( arrayElement.get( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
+                             "mock error1", null, null );
+
+        verifyJsonErrorNode( arrayElement.get( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
+                             "mock error2", null, null );
+    }
+
+    @Test
+    public void testToJsonResponseWithErrorInfo() throws Exception {
+
+        String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
+        testJsonResponse( new RestconfDocumentedException( new RestconfError(
+                                               ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+                                               "mock error", "mock-app-tag", errorInfo ) ),
+                          Status.BAD_REQUEST, ErrorType.APPLICATION,
+                          ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
+                          new ComplexErrorInfoVerifier( ImmutableMap.of(
+                                                "session-id", "123", "address", "1.2.3.4" ) ) );
+    }
+
+    @Test
+    public void testToJsonResponseWithExceptionCause() throws Exception {
+
+        Exception cause = new Exception( "mock exception cause" );
+        testJsonResponse( new RestconfDocumentedException( "mock error", cause ),
+                          Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
+                          ErrorTag.OPERATION_FAILED, "mock error", null,
+                          new SimpleErrorInfoVerifier( cause.getMessage() ) );
+    }
+
+    void testXMLResponse( RestconfDocumentedException ex, Status expStatus, ErrorType expErrorType,
+                          ErrorTag expErrorTag, String expErrorMessage,
+                          String expErrorAppTag, ErrorInfoVerifier errorInfoVerifier ) throws Exception
+    {
+        stageMockEx( ex );
+
+        Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
+
+        InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, expStatus );
+
+        verifyXMLResponseBody( stream, expErrorType, expErrorTag, expErrorMessage,
+                               expErrorAppTag, errorInfoVerifier );
+    }
+
+    @Test
+    public void testToXMLResponseWithMessageOnly() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error" ), Status.INTERNAL_SERVER_ERROR,
+                         ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null );
+
+        // To test verification code
+//        String xml =
+//            "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+
+//            "  <error>" +
+//            "    <error-type>application</error-type>"+
+//            "    <error-tag>operation-failed</error-tag>"+
+//            "    <error-message>An error occurred</error-message>"+
+//            "    <error-info>" +
+//            "      <session-id>123</session-id>" +
+//            "      <address>1.2.3.4</address>" +
+//            "    </error-info>" +
+//            "  </error>" +
+//            "</errors>";
+//
+//        verifyXMLResponseBody( new java.io.StringBufferInputStream(xml), ErrorType.APPLICATION,
+//                ErrorTag.OPERATION_FAILED, "An error occurred", null,
+//                com.google.common.collect.ImmutableMap.of( "session-id", "123", "address", "1.2.3.4" ) );
+    }
+
+    @Test
+    public void testToXMLResponseWithInUseErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.IN_USE ),
+                         Status.CONFLICT, ErrorType.PROTOCOL,
+                         ErrorTag.IN_USE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithInvalidValueErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.RPC,
+                                                           ErrorTag.INVALID_VALUE ),
+                         Status.BAD_REQUEST, ErrorType.RPC,
+                         ErrorTag.INVALID_VALUE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithTooBigErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.TRANSPORT,
+                                                           ErrorTag.TOO_BIG ),
+                         Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT,
+                         ErrorTag.TOO_BIG, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.MISSING_ATTRIBUTE ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithBadAttributeErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.BAD_ATTRIBUTE ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.BAD_ATTRIBUTE, "mock error", null, null );
+    }
+    @Test
+    public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.UNKNOWN_ATTRIBUTE ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithBadElementErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.BAD_ELEMENT ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.BAD_ELEMENT, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithUnknownElementErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.UNKNOWN_ELEMENT ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.UNKNOWN_NAMESPACE ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.MALFORMED_MESSAGE ),
+                         Status.BAD_REQUEST, ErrorType.PROTOCOL,
+                         ErrorTag.MALFORMED_MESSAGE, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.ACCESS_DENIED ),
+                         Status.FORBIDDEN, ErrorType.PROTOCOL,
+                         ErrorTag.ACCESS_DENIED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithLockDeniedErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.LOCK_DENIED ),
+                          Status.CONFLICT, ErrorType.PROTOCOL,
+                          ErrorTag.LOCK_DENIED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.RESOURCE_DENIED ),
+                         Status.CONFLICT, ErrorType.PROTOCOL,
+                         ErrorTag.RESOURCE_DENIED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.ROLLBACK_FAILED ),
+                         Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
+                         ErrorTag.ROLLBACK_FAILED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithDataExistsErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.DATA_EXISTS ),
+                         Status.CONFLICT, ErrorType.PROTOCOL,
+                         ErrorTag.DATA_EXISTS, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.DATA_MISSING ),
+                         Status.CONFLICT, ErrorType.PROTOCOL,
+                         ErrorTag.DATA_MISSING, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.OPERATION_NOT_SUPPORTED ),
+                         Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL,
+                         ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithOperationFailedErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.OPERATION_FAILED ),
+                         Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
+                         ErrorTag.OPERATION_FAILED, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithPartialOperationErrorTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( "mock error", ErrorType.PROTOCOL,
+                                                           ErrorTag.PARTIAL_OPERATION ),
+                         Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL,
+                         ErrorTag.PARTIAL_OPERATION, "mock error", null, null );
+    }
+
+    @Test
+    public void testToXMLResponseWithErrorAppTag() throws Exception {
+
+        testXMLResponse( new RestconfDocumentedException( new RestconfError(
+                                              ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+                                              "mock error", "mock-app-tag" ) ),
+                         Status.BAD_REQUEST, ErrorType.APPLICATION,
+                         ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null );
+    }
+
+    @Test
+    public void testToXMLResponseWithErrorInfo() throws Exception {
+
+        String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>";
+        testXMLResponse( new RestconfDocumentedException( new RestconfError(
+                                               ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
+                                               "mock error", "mock-app-tag", errorInfo ) ),
+                         Status.BAD_REQUEST, ErrorType.APPLICATION,
+                         ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag",
+                         new ComplexErrorInfoVerifier( ImmutableMap.of(
+                                              "session-id", "123", "address", "1.2.3.4" ) ) );
+    }
+
+    @Test
+    public void testToXMLResponseWithExceptionCause() throws Exception {
+
+        Exception cause = new Exception( "mock exception cause" );
+        testXMLResponse( new RestconfDocumentedException( "mock error", cause ),
+                         Status.INTERNAL_SERVER_ERROR, ErrorType.APPLICATION,
+                         ErrorTag.OPERATION_FAILED, "mock error", null,
+                         new SimpleErrorInfoVerifier( cause.getMessage() ) );
+    }
+
+    @Test
+    public void testToXMLResponseWithMultipleErrors() throws Exception {
+
+        List<RestconfError> errorList = Arrays.asList(
+                new RestconfError( ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1" ),
+                new RestconfError( ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2" ) );
+        stageMockEx( new RestconfDocumentedException( errorList ) );
+
+        Response resp = target("/operational/foo").request( MediaType.APPLICATION_XML ).get();
+
+        InputStream stream = verifyResponse( resp, MediaType.APPLICATION_XML, Status.CONFLICT );
+
+        Document doc = parseXMLDocument( stream );
+
+        NodeList children = getXMLErrorList( doc, 2 );
+
+        verifyXMLErrorNode( children.item( 0 ), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
+                            "mock error1", null, null );
+
+        verifyXMLErrorNode( children.item( 1 ), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED,
+                            "mock error2", null, null );
+    }
+
+    @Test
+    public void testToResponseWithAcceptHeader() throws Exception {
+
+        stageMockEx( new RestconfDocumentedException( "mock error" ) );
+
+        Response resp = target("/operational/foo")
+                                  .request().header( "Accept", MediaType.APPLICATION_JSON ).get();
+
+        InputStream stream = verifyResponse( resp, MediaType.APPLICATION_JSON,
+                                             Status.INTERNAL_SERVER_ERROR );
+
+        verifyJsonResponseBody( stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error",
+                                null, null );
+    }
+
+    @Test
+    public void testToResponseWithStatusOnly() throws Exception {
+
+        // The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data
+
+        when( mockRestConf.readOperationalData( any( String.class ) ) )
+            .thenReturn( new StructuredData( null, null, null ) );
+
+        Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
+
+        verifyResponse( resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND );
+    }
+
+    InputStream verifyResponse( Response resp, String expMediaType, Status expStatus ) {
+        assertEquals( "getMediaType", MediaType.valueOf( expMediaType ), resp.getMediaType() );
+        assertEquals( "getStatus", expStatus.getStatusCode(), resp.getStatus() );
+
+        Object entity = resp.getEntity();
+        assertEquals( "Response entity", true, entity instanceof InputStream );
+        InputStream stream = (InputStream)entity;
+        return stream;
+    }
+
+    void verifyJsonResponseBody( InputStream stream, ErrorType expErrorType, ErrorTag expErrorTag,
+                                 String expErrorMessage, String expErrorAppTag,
+                                 ErrorInfoVerifier errorInfoVerifier ) throws Exception {
+
+        JsonArray arrayElement = parseJsonErrorArrayElement( stream );
+
+        assertEquals( "\"error\" Json array element length", 1, arrayElement.size() );
+
+        verifyJsonErrorNode( arrayElement.get( 0 ),  expErrorType, expErrorTag, expErrorMessage,
+                             expErrorAppTag, errorInfoVerifier );
+    }
+
+    private JsonArray parseJsonErrorArrayElement( InputStream stream ) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ByteStreams.copy( stream, bos );
+
+        System.out.println("JSON: "+bos.toString());
+
+        JsonParser parser = new JsonParser();
+        JsonElement rootElement;
+
+        try {
+            rootElement = parser.parse(
+                          new InputStreamReader( new ByteArrayInputStream( bos.toByteArray() ) ) );
+        }
+        catch( Exception e ) {
+            throw new IllegalArgumentException( "Invalid JSON response:\n" + bos.toString(), e );
+        }
+
+        assertTrue( "Root element of Json is not an Object", rootElement.isJsonObject() );
+
+        Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet();
+        assertEquals( "Json Object element set count", 1, errorsEntrySet.size() );
+
+        Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next();
+        JsonElement errorsElement = errorsEntry.getValue();
+        assertEquals( "First Json element name", "errors", errorsEntry.getKey() );
+        assertTrue( "\"errors\" Json element is not an Object", errorsElement.isJsonObject() );
+
+        Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet();
+        assertEquals( "Root \"errors\" element child count", 1, errorListEntrySet.size() );
+
+        JsonElement errorListElement = errorListEntrySet.iterator().next().getValue();
+        assertEquals( "\"errors\" child Json element name", "error",
+                      errorListEntrySet.iterator().next().getKey() );
+        assertTrue( "\"error\" Json element is not an Array", errorListElement.isJsonArray() );
+
+        return errorListElement.getAsJsonArray();
+    }
+
+    void verifyJsonErrorNode( JsonElement errorEntryElement, ErrorType expErrorType, ErrorTag expErrorTag,
+                              String expErrorMessage, String expErrorAppTag,
+                              ErrorInfoVerifier errorInfoVerifier ) {
+
+        JsonElement errorInfoElement = null;
+        Map<String, String> actualErrorInfo = null;
+        Map<String, String> leafMap = Maps.newHashMap();
+        for( Entry<String, JsonElement> entry: errorEntryElement.getAsJsonObject().entrySet() ) {
+            String leafName = entry.getKey();
+            JsonElement leafElement = entry.getValue();
+
+            if( "error-info".equals( leafName ) ) {
+                assertNotNull( "Found unexpected \"error-info\" element", errorInfoVerifier );
+                errorInfoElement = leafElement;
+            }
+            else {
+                assertTrue( "\"error\" leaf Json element " + leafName +
+                            " is not a Primitive", leafElement.isJsonPrimitive() );
+
+                leafMap.put( leafName, leafElement.getAsString() );
+            }
+        }
+
+        assertEquals( "error-type", expErrorType.getErrorTypeTag(), leafMap.remove( "error-type" ) );
+        assertEquals( "error-tag", expErrorTag.getTagValue(), leafMap.remove( "error-tag" ) );
+
+        verifyOptionalJsonLeaf( leafMap.remove( "error-message" ), expErrorMessage, "error-message" );
+        verifyOptionalJsonLeaf( leafMap.remove( "error-app-tag" ), expErrorAppTag, "error-app-tag" );
+
+        if( !leafMap.isEmpty() ) {
+            fail( "Found unexpected Json leaf elements for \"error\" element: " + leafMap );
+        }
+
+        if( errorInfoVerifier != null ) {
+            assertNotNull( "Missing \"error-info\" element", errorInfoElement );
+            errorInfoVerifier.verifyJson( errorInfoElement );
+        }
+    }
+
+    void verifyOptionalJsonLeaf( String actualValue, String expValue, String tagName ) {
+        if( expValue != null ) {
+            assertEquals( tagName, expValue, actualValue );
+        }
+        else {
+            assertNull( "Found unexpected \"error\" leaf entry for: " + tagName, actualValue );
+        }
+    }
+
+    void verifyXMLResponseBody( InputStream stream, ErrorType expErrorType, ErrorTag expErrorTag,
+                                String expErrorMessage, String expErrorAppTag,
+                                ErrorInfoVerifier errorInfoVerifier )
+                                                                    throws Exception {
+
+        Document doc = parseXMLDocument( stream );
+
+        NodeList children = getXMLErrorList( doc, 1 );
+
+        verifyXMLErrorNode( children.item( 0 ), expErrorType, expErrorTag, expErrorMessage,
+                            expErrorAppTag, errorInfoVerifier );
+    }
+
+    private Document parseXMLDocument( InputStream stream ) throws IOException {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ByteStreams.copy( stream, bos );
+
+        System.out.println("XML: "+bos.toString());
+
+        Document doc = null;
+        try {
+            doc = factory.newDocumentBuilder().parse( new ByteArrayInputStream( bos.toByteArray() ) );
+        }
+        catch( Exception e ) {
+            throw new IllegalArgumentException( "Invalid XML response:\n" + bos.toString(), e );
+        }
+        return doc;
+    }
+
+    void verifyXMLErrorNode( Node errorNode, ErrorType expErrorType, ErrorTag expErrorTag,
+                             String expErrorMessage, String expErrorAppTag,
+                             ErrorInfoVerifier errorInfoVerifier ) throws Exception {
+
+        String errorType = (String)ERROR_TYPE.evaluate( errorNode, XPathConstants.STRING );
+        assertEquals( "error-type", expErrorType.getErrorTypeTag(), errorType );
+
+        String errorTag = (String)ERROR_TAG.evaluate( errorNode, XPathConstants.STRING );
+        assertEquals( "error-tag", expErrorTag.getTagValue(), errorTag );
+
+        verifyOptionalXMLLeaf( errorNode, ERROR_MESSAGE, expErrorMessage, "error-message" );
+        verifyOptionalXMLLeaf( errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag" );
+
+        Node errorInfoNode = (Node)ERROR_INFO.evaluate( errorNode, XPathConstants.NODE );
+        if( errorInfoVerifier != null ) {
+            assertNotNull( "Missing \"error-info\" node", errorInfoNode );
+
+            errorInfoVerifier.verifyXML( errorInfoNode );
+        }
+        else {
+            assertNull( "Found unexpected \"error-info\" node", errorInfoNode );
+        }
+    }
+
+    void verifyOptionalXMLLeaf( Node fromNode, XPathExpression xpath, String expValue,
+                                String tagName ) throws Exception {
+        if( expValue != null ) {
+            String actual = (String)xpath.evaluate( fromNode, XPathConstants.STRING );
+            assertEquals( tagName, expValue, actual );
+        }
+        else {
+            assertNull( "Found unexpected \"error\" leaf entry for: " + tagName,
+                        xpath.evaluate( fromNode, XPathConstants.NODE ) );
+        }
+    }
+
+    NodeList getXMLErrorList( Node fromNode, int count ) throws Exception {
+        NodeList errorList = (NodeList)ERROR_LIST.evaluate( fromNode, XPathConstants.NODESET );
+        assertNotNull( "Root errors node is empty", errorList );
+        assertEquals( "Root errors node child count", count, errorList.getLength() );
+        return errorList;
+    }
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java
new file mode 100644 (file)
index 0000000..70ad768
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+* Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v1.0 which accompanies this distribution,
+* and is available at http://www.eclipse.org/legal/epl-v10.html
+*/
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
+import static org.opendaylight.controller.sal.common.util.RpcErrors.getRpcError;
+
+import org.opendaylight.controller.sal.restconf.impl.RestconfError;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcError;
+
+/**
+ * Unit tests for RestconfError.
+ *
+ * @author Devin Avery
+ * @author Thomas Pantelis
+ *
+ */
+public class RestconfErrorTest {
+
+    static class Contains extends BaseMatcher<String> {
+
+        private final String text;
+
+        public Contains( String text ) {
+            this.text = text;
+        }
+
+        @Override
+        public void describeTo( Description desc ) {
+            desc.appendText( "contains " ).appendValue( text );
+        }
+
+        @Override
+        public boolean matches( Object arg ) {
+            return arg != null && arg.toString().contains( text );
+        }
+    }
+
+    @Test
+    public void testErrorTagValueOf()
+    {
+        assertEquals( ErrorTag.IN_USE,
+                ErrorTag.valueOfCaseInsensitive( ErrorTag.IN_USE.getTagValue() ) );
+    }
+
+    @Test
+    public void testErrorTagValueOfIsLowercase()
+    {
+        assertEquals( "in-use",
+                ErrorTag.IN_USE.getTagValue() );
+    }
+
+    @Test
+    public void testErrorTypeGetErrorTypeTagIsLowerCase()
+    {
+       assertEquals( ErrorType.APPLICATION.name().toLowerCase(),
+               ErrorType.APPLICATION.getErrorTypeTag() );
+    }
+
+    @Test
+    public void testErrorTypeValueOf()
+    {
+       assertEquals( ErrorType.APPLICATION,
+                     ErrorType.valueOfCaseInsensitive( ErrorType.APPLICATION.getErrorTypeTag() ) );
+    }
+
+    @Test
+    public void testErrorTagStatusCodes()
+    {
+        Map<String,Status> lookUpMap = new HashMap<String,Status>();
+
+        lookUpMap.put( "in-use", Status.fromStatusCode(409));
+        lookUpMap.put( "invalid-value", Status.fromStatusCode(400));
+        lookUpMap.put( "too-big", Status.fromStatusCode(413));
+        lookUpMap.put( "missing-attribute", Status.fromStatusCode(400));
+        lookUpMap.put( "bad-attribute", Status.fromStatusCode(400));
+        lookUpMap.put( "unknown-attribute", Status.fromStatusCode(400));
+        lookUpMap.put( "bad-element", Status.fromStatusCode(400));
+        lookUpMap.put( "unknown-element", Status.fromStatusCode(400));
+        lookUpMap.put( "unknown-namespace", Status.fromStatusCode(400));
+        lookUpMap.put( "access-denied", Status.fromStatusCode(403));
+        lookUpMap.put( "lock-denied", Status.fromStatusCode(409));
+        lookUpMap.put( "resource-denied", Status.fromStatusCode(409));
+        lookUpMap.put( "rollback-failed", Status.fromStatusCode(500));
+        lookUpMap.put( "data-exists", Status.fromStatusCode(409));
+        lookUpMap.put( "data-missing", Status.fromStatusCode(409));
+        lookUpMap.put( "operation-not-supported", Status.fromStatusCode(501));
+        lookUpMap.put( "operation-failed", Status.fromStatusCode(500));
+        lookUpMap.put( "partial-operation", Status.fromStatusCode(500));
+        lookUpMap.put( "malformed-message", Status.fromStatusCode(400));
+
+        for( ErrorTag tag : ErrorTag.values() )
+        {
+            Status expectedStatusCode = lookUpMap.get( tag.getTagValue() );
+            assertNotNull( "Failed to find " + tag.getTagValue(), expectedStatusCode );
+            assertEquals( "Status Code does not match", expectedStatusCode, tag.getStatusCode() );
+        }
+    }
+
+    @Test
+    public void testRestConfDocumentedException_NoCause()
+    {
+        String expectedMessage = "Message";
+        ErrorType expectedErrorType = ErrorType.RPC;
+        ErrorTag expectedErrorTag = ErrorTag.IN_USE;
+        RestconfError e =
+                new RestconfError( expectedErrorType,
+                                                 expectedErrorTag, expectedMessage );
+
+        validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag,
+                              null, (String)null, e);
+    }
+
+    @Test
+    public void testRestConfDocumentedException_WithAppTag()
+    {
+        String expectedMessage = "Message";
+        ErrorType expectedErrorType = ErrorType.RPC;
+        ErrorTag expectedErrorTag = ErrorTag.IN_USE;
+        String expectedErrorAppTag = "application.tag";
+
+        RestconfError e =
+                new RestconfError( expectedErrorType,
+                                                 expectedErrorTag, expectedMessage, expectedErrorAppTag );
+
+        validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag,
+                              expectedErrorAppTag, (String)null, e);
+    }
+
+    @Test
+    public void testRestConfDocumentedException_WithAppTagErrorInfo()
+    {
+        String expectedMessage = "Message";
+        ErrorType expectedErrorType = ErrorType.RPC;
+        ErrorTag expectedErrorTag = ErrorTag.IN_USE;
+        String expectedErrorAppTag = "application.tag";
+        String errorInfo = "<extra><sessionid>session.id</sessionid></extra>";
+
+        RestconfError e = new RestconfError( expectedErrorType,
+                                             expectedErrorTag,
+                                             expectedMessage,
+                                             expectedErrorAppTag,
+                                             errorInfo );
+
+        validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag,
+                expectedErrorAppTag, errorInfo, e);
+    }
+
+    @Test
+    public void testRestConfErrorWithRpcError() {
+
+        // All fields set
+        RpcError rpcError = getRpcError( "mock app-tag", ErrorTag.BAD_ATTRIBUTE.getTagValue(),
+                                         "mock error-info", RpcError.ErrorSeverity.ERROR,
+                                         "mock error-message", RpcError.ErrorType.PROTOCOL,
+                                         new Exception( "mock cause" ) );
+
+        validateRestConfError( "mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE,
+                               "mock app-tag", "mock error-info",
+                               new RestconfError( rpcError ) );
+
+        // All fields set except 'info' - expect error-info set to 'cause'
+        rpcError = getRpcError( "mock app-tag", ErrorTag.BAD_ATTRIBUTE.getTagValue(),
+                                null, RpcError.ErrorSeverity.ERROR,
+                                "mock error-message", RpcError.ErrorType.PROTOCOL,
+                                new Exception( "mock cause" ) );
+
+        validateRestConfError( "mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE,
+                               "mock app-tag", new Contains( "mock cause" ),
+                               new RestconfError( rpcError ) );
+
+        // Some fields set - expect error-info set to ErrorSeverity
+        rpcError = getRpcError( null, ErrorTag.ACCESS_DENIED.getTagValue(),
+                                null, RpcError.ErrorSeverity.ERROR,
+                                null, RpcError.ErrorType.RPC, null );
+
+        validateRestConfError( null, ErrorType.RPC, ErrorTag.ACCESS_DENIED,
+                               null, "<severity>error</severity>",
+                               new RestconfError( rpcError ) );
+
+        // 'tag' field not mapped to ErrorTag - expect error-tag set to OPERATION_FAILED
+        rpcError = getRpcError( null, "not mapped",
+                                null, RpcError.ErrorSeverity.WARNING,
+                                null, RpcError.ErrorType.TRANSPORT, null );
+
+        validateRestConfError( null, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED,
+                               null, "<severity>warning</severity>",
+                               new RestconfError( rpcError ) );
+
+        // No fields set - edge case
+        rpcError = getRpcError( null, null, null, null, null, null, null );
+
+        validateRestConfError( null, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
+                               null, (String)null, new RestconfError( rpcError ) );
+    }
+
+    private void validateRestConfError(String expectedMessage, ErrorType expectedErrorType,
+            ErrorTag expectedErrorTag, String expectedErrorAppTag, String errorInfo, RestconfError e) {
+
+        validateRestConfError( expectedMessage, expectedErrorType, expectedErrorTag,
+                               expectedErrorAppTag, equalTo( errorInfo ), e );
+    }
+
+    private void validateRestConfError(String expectedMessage, ErrorType expectedErrorType,
+            ErrorTag expectedErrorTag, String expectedErrorAppTag,
+            Matcher<String> errorInfoMatcher, RestconfError e) {
+
+        assertEquals( "getErrorMessage", expectedMessage, e.getErrorMessage() );
+        assertEquals( "getErrorType", expectedErrorType, e.getErrorType() );
+        assertEquals( "getErrorTag", expectedErrorTag, e.getErrorTag() );
+        assertEquals( "getErrorAppTag", expectedErrorAppTag, e.getErrorAppTag() );
+        assertThat( "getErrorInfo", e.getErrorInfo(), errorInfoMatcher );
+        e.toString(); // really just checking for NPE etc. Don't care about contents.
+    }
+}
index eef9e414e93e75d90ec29719d7f93b85952c9400..33d4b325bef2c6abc6424c044e8a572acb619ac2 100644 (file)
@@ -26,7 +26,7 @@ import org.opendaylight.controller.sal.core.api.mount.MountService;
 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -67,15 +67,13 @@ public class URITest {
 
     @Test
     public void testToInstanceIdentifierListWithNullKey() {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request");
+        exception.expect(RestconfDocumentedException.class);
         controllerContext.toInstanceIdentifier("simple-nodes:user/null/boo");
     }
 
     @Test
     public void testToInstanceIdentifierListWithMissingKey() {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request");
+        exception.expect(RestconfDocumentedException.class);
         controllerContext.toInstanceIdentifier("simple-nodes:user/foo");
     }
 
@@ -96,29 +94,25 @@ public class URITest {
 
     @Test
     public void testToInstanceIdentifierChoiceException() {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request");
+        exception.expect(RestconfDocumentedException.class);
         controllerContext.toInstanceIdentifier("simple-nodes:food/snack");
     }
 
     @Test
     public void testToInstanceIdentifierCaseException() {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request");
+        exception.expect(RestconfDocumentedException.class);
         controllerContext.toInstanceIdentifier("simple-nodes:food/sports-arena");
     }
 
     @Test
     public void testToInstanceIdentifierChoiceCaseException() {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request");
+        exception.expect(RestconfDocumentedException.class);
         controllerContext.toInstanceIdentifier("simple-nodes:food/snack/sports-arena");
     }
-    
+
     @Test
     public void testToInstanceIdentifierWithoutNode() {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request");
+        exception.expect(RestconfDocumentedException.class);
         controllerContext.toInstanceIdentifier("simple-nodes");
     }
 
@@ -142,24 +136,22 @@ public class URITest {
 
     @Test
     public void testMountPointWithoutMountService() throws FileNotFoundException {
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 503 Service Unavailable"); 
-        
+        exception.expect(RestconfDocumentedException.class);
+
         controllerContext.setMountService(null);
         InstanceIdWithSchemaNode instanceIdentifier = controllerContext
                 .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class/student/name");
     }
-    
+
     @Test
     public void testMountPointWithoutMountPointSchema() {
         initMountService(false);
-        exception.expect(ResponseException.class);
-        exception.expectMessage("HTTP 400 Bad Request"); 
-        
+        exception.expect(RestconfDocumentedException.class);
+
         InstanceIdWithSchemaNode instanceIdentifier = controllerContext
                 .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class");
     }
-    
+
     public void initMountService(boolean withSchema) {
         MountService mountService = mock(MountService.class);
         controllerContext.setMountService(mountService);
diff --git a/opendaylight/md-sal/sal-schema-repository-api/.gitignore b/opendaylight/md-sal/sal-schema-repository-api/.gitignore
deleted file mode 100644 (file)
index ea8c4bf..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/target
diff --git a/opendaylight/md-sal/sal-schema-repository-api/pom.xml b/opendaylight/md-sal/sal-schema-repository-api/pom.xml
deleted file mode 100644 (file)
index a6e5c78..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<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">\r
-  <modelVersion>4.0.0</modelVersion>\r
-  <parent>\r
-    <groupId>org.opendaylight.controller</groupId>\r
-    <artifactId>sal-parent</artifactId>\r
-    <version>1.0-SNAPSHOT</version>\r
-  </parent>\r
-  <artifactId>sal-schema-repository-api</artifactId>\r
-  <scm>\r
-    <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>\r
-    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>\r
-    <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>\r
-  </scm>\r
-\r
-  <dependencies>\r
-      <dependency>\r
-          <groupId>org.opendaylight.controller</groupId>\r
-          <artifactId>yang-model-api</artifactId>\r
-      </dependency>\r
-  </dependencies>\r
-</project>\r
diff --git a/opendaylight/md-sal/sal-schema-repository-api/src/main/java/org/opendaylight/controller/sal/schema/api/package-info.java b/opendaylight/md-sal/sal-schema-repository-api/src/main/java/org/opendaylight/controller/sal/schema/api/package-info.java
deleted file mode 100644 (file)
index 39387f1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-/*\r
-  * Copyright (c) 2013 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
-package org.opendaylight.controller.sal.schema.api;
\ No newline at end of file
index b4da5a3d226f05bfd02fe21890ea0307e7efb2ce..2ecd7e7b684fe8bdafd6af17527cbb21daeee83f 100644 (file)
@@ -37,7 +37,9 @@ 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.ErrorType;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -157,14 +159,15 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
                 LOG.info( "Toaster is already making toast" );
 
                 RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
-                        RpcErrors.getRpcError( null, null, null, null,
-                                               "Toaster is busy", null, null ) ) );
+                        RpcErrors.getRpcError( "", "in-use", null, ErrorSeverity.WARNING,
+                                               "Toaster is busy", ErrorType.APPLICATION, null ) ) );
                 return Futures.immediateFuture(result);
             }
             else if( outOfBread() ) {
                 RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
-                        RpcErrors.getRpcError( null, null, null, null,
-                                               "Toaster is out of bread", null, null ) ) );
+                        RpcErrors.getRpcError( "out-of-stock", "resource-denied", null, null,
+                                               "Toaster is out of bread",
+                                               ErrorType.APPLICATION, null ) ) );
                 return Futures.immediateFuture(result);
             }
             else {
index cf9b4c159746e15434be1096d0caf946e2417ec3..ff1d719c57877c2df4710701d9d6f0bd65075b07 100644 (file)
@@ -34,6 +34,7 @@ import org.opendaylight.controller.netconf.confignetconfconnector.operations.edi
 import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -43,11 +44,15 @@ public final class InstanceConfig {
     private static final Logger logger = LoggerFactory.getLogger(InstanceConfig.class);
 
     private final Map<String, AttributeIfc> yangToAttrConfig;
+    private final String nullableDummyContainerName;
     private final Map<String, AttributeIfc> jmxToAttrConfig;
     private final ConfigRegistryClient configRegistryClient;
 
-    public InstanceConfig(ConfigRegistryClient configRegistryClient, Map<String, AttributeIfc> yangNamesToAttributes) {
+    public InstanceConfig(ConfigRegistryClient configRegistryClient, Map<String, AttributeIfc> yangNamesToAttributes,
+                          String nullableDummyContainerName) {
+
         this.yangToAttrConfig = yangNamesToAttributes;
+        this.nullableDummyContainerName = nullableDummyContainerName;
         this.jmxToAttrConfig = reverseMap(yangNamesToAttributes);
         this.configRegistryClient = configRegistryClient;
     }
@@ -83,20 +88,24 @@ public final class InstanceConfig {
     }
 
     public Element toXml(ObjectName on, String namespace, Document document, Element rootElement) {
-
         Map<String, AttributeWritingStrategy> strats = new ObjectXmlWriter().prepareWriting(yangToAttrConfig, document);
-
         Map<String, Object> mappedConfig = getMappedConfiguration(on);
-
+        Element parentElement;
+        if (nullableDummyContainerName != null) {
+            Element dummyElement = XmlUtil.createElement(document, nullableDummyContainerName, Optional.of(namespace));
+            rootElement.appendChild(dummyElement);
+            parentElement = dummyElement;
+        } else {
+            parentElement = rootElement;
+        }
         for (Entry<String, ?> mappingEntry : mappedConfig.entrySet()) {
             try {
-                strats.get(mappingEntry.getKey()).writeElement(rootElement, namespace, mappingEntry.getValue());
+                strats.get(mappingEntry.getKey()).writeElement(parentElement, namespace, mappingEntry.getValue());
             } catch (Exception e) {
                 throw new IllegalStateException("Unable to write value " + mappingEntry.getValue() + " for attribute "
                         + mappingEntry.getValue(), e);
             }
         }
-
         return rootElement;
     }
 
@@ -132,22 +141,46 @@ public final class InstanceConfig {
         Map<String, AttributeReadingStrategy> strats = new ObjectXmlReader().prepareReading(yangToAttrConfig, identityMap);
         List<XmlElement> recognisedChildren = Lists.newArrayList();
 
-        XmlElement type;
-        XmlElement name;
-        type = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
-        name = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
-        List<XmlElement> typeAndName = Lists.newArrayList(type, name);
+        XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
+        XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
+        List<XmlElement> typeAndNameElements = Lists.newArrayList(typeElement, nameElement);
+
+        // if dummy container was defined in yang, set moduleElement to its content
+        if (nullableDummyContainerName != null) {
+            int size = moduleElement.getChildElements().size();
+            int expectedChildNodes = 1 + typeAndNameElements.size();
+            if (size > expectedChildNodes) {
+                throw new NetconfDocumentedException("Error reading module " + typeElement.getTextContent() + " : " +
+                        nameElement.getTextContent() + " - Expected " + expectedChildNodes +" child nodes, " +
+                        "one of them with name " + nullableDummyContainerName +
+                        ", got " + size + " elements.");
+            }
+            if (size == expectedChildNodes) {
+                try {
+                    moduleElement = moduleElement.getOnlyChildElement(nullableDummyContainerName, moduleNamespace);
+                } catch (NetconfDocumentedException e) {
+                    throw new NetconfDocumentedException("Error reading module " + typeElement.getTextContent() + " : " +
+                            nameElement.getTextContent() + " - Expected child node with name " + nullableDummyContainerName +
+                            "." + e.getMessage());
+                }
+            } // else 2 elements, no need to descend
+        }
 
         for (Entry<String, AttributeReadingStrategy> readStratEntry : strats.entrySet()) {
             List<XmlElement> configNodes = getConfigNodes(moduleElement, moduleNamespace, readStratEntry.getKey(),
-                    recognisedChildren, typeAndName);
+                    recognisedChildren, typeAndNameElements);
             AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes);
             retVal.put(readStratEntry.getKey(), readElement);
         }
 
-        recognisedChildren.addAll(typeAndName);
-        moduleElement.checkUnrecognisedElements(recognisedChildren);
-
+        recognisedChildren.addAll(typeAndNameElements);
+        try {
+            moduleElement.checkUnrecognisedElements(recognisedChildren);
+        } catch (NetconfDocumentedException e) {
+            throw new NetconfDocumentedException("Error reading module " + typeElement.getTextContent() + " : " +
+                    nameElement.getTextContent() + " - " +
+                    e.getMessage(), e.getErrorType(), e.getErrorTag(),e.getErrorSeverity(),e.getErrorInfo());
+        }
         // TODO: add check for conflicts between global and local edit strategy
         String perInstanceEditStrategy = moduleElement.getAttribute(XmlNetconfConstants.OPERATION_ATTR_KEY,
                 XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
index ffc69eb8efd2baa4fc1b6399a2a66e9ac058331f..319f539c25f02e9cbddf28b62288b5805fea8fd2 100644 (file)
@@ -273,8 +273,8 @@ public class EditConfig extends AbstractConfigNetconfOperation {
                 String moduleName = moduleNameToMbe.getKey();
                 ModuleMXBeanEntry moduleMXBeanEntry = moduleNameToMbe.getValue();
 
-                ModuleConfig moduleConfig = new ModuleConfig(moduleName, new InstanceConfig(configRegistryClient,
-                        moduleMXBeanEntry.getAttributes()));
+                ModuleConfig moduleConfig = new ModuleConfig(moduleName,
+                        new InstanceConfig(configRegistryClient,moduleMXBeanEntry.getAttributes(), moduleMXBeanEntry.getNullableDummyContainerName()));
 
                 Map<String, ModuleConfig> moduleNameToModuleConfig = namespaceToModuleNameToModuleConfig.get(namespace);
                 if(moduleNameToModuleConfig == null) {
index 5e44427da3ce4ba006a1c6085466bbb05d28f22b..48b4bbc2a825e6fd00a3d31575ffac69c0b22630 100644 (file)
@@ -62,7 +62,7 @@ public class Get extends AbstractConfigNetconfOperation {
                 Map<RuntimeBeanEntry, InstanceConfig> cache = Maps.newHashMap();
                 RuntimeBeanEntry root = null;
                 for (RuntimeBeanEntry rbe : mbe.getRuntimeBeans()) {
-                    cache.put(rbe, new InstanceConfig(configRegistryClient, rbe.getYangPropertiesToTypesMap()));
+                    cache.put(rbe, new InstanceConfig(configRegistryClient, rbe.getYangPropertiesToTypesMap(), mbe.getNullableDummyContainerName()));
                     if (rbe.isRoot()){
                         root = rbe;
                     }
index 54b15a85b29f18a640a6e3c8c278b37b8cafa20d..41301543a33f54754f0ccfc7bda24f2a9b818f74 100644 (file)
@@ -24,6 +24,7 @@ import static org.opendaylight.controller.netconf.util.xml.XmlUtil.readXmlToElem
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -39,6 +40,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import javax.management.InstanceAlreadyExistsException;
 import javax.management.InstanceNotFoundException;
@@ -75,6 +77,7 @@ import org.opendaylight.controller.config.yang.test.impl.IdentityTestModuleFacto
 import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory;
 import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleMXBean;
 import org.opendaylight.controller.config.yang.test.impl.Peers;
+import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
@@ -93,7 +96,6 @@ import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity1;
@@ -124,6 +126,7 @@ public class NetconfMappingTest extends AbstractConfigTest {
     private NetconfTestImplModuleFactory factory;
     private DepTestImplModuleFactory factory2;
     private IdentityTestModuleFactory factory3;
+    private TestImplModuleFactory factory4;
 
     @Mock
     YangStoreSnapshot yangStoreSnapshot;
@@ -144,8 +147,9 @@ public class NetconfMappingTest extends AbstractConfigTest {
         this.factory = new NetconfTestImplModuleFactory();
         this.factory2 = new DepTestImplModuleFactory();
         this.factory3 = new IdentityTestModuleFactory();
+        factory4 = new TestImplModuleFactory();
         super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, this.factory, this.factory2,
-                this.factory3));
+                this.factory3, factory4));
 
         transactionProvider = new TransactionProvider(this.configRegistryClient, NETCONF_SESSION_ID);
     }
@@ -423,42 +427,42 @@ public class NetconfMappingTest extends AbstractConfigTest {
         assertEquals(2, afterReplace);
     }
 
-    @Test(expected = NetconfDocumentedException.class)
+    @Test
     public void testSameAttrDifferentNamespaces() throws Exception {
         try {
             edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml");
+            fail();
         } catch (NetconfDocumentedException e) {
             String message = e.getMessage();
-            assertContainsString(message, "Element simple-long-2 present multiple times with different namespaces");
+            assertContainsString(message, "Element simpleInt present multiple times with different namespaces");
             assertContainsString(message, TEST_NAMESPACE);
             assertContainsString(message, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
-            throw e;
         }
     }
 
-    @Test(expected = NetconfDocumentedException.class)
+    @Test
     public void testDifferentNamespaceInTO() throws Exception {
         try {
             edit("netconfMessages/namespaces/editConfig_differentNamespaceTO.xml");
+            fail();
         } catch (NetconfDocumentedException e) {
             String message = e.getMessage();
             assertContainsString(message, "Unrecognised elements");
             assertContainsString(message, "simple-int2");
             assertContainsString(message, "dto_d");
-            throw e;
         }
     }
 
-    @Test(expected = NetconfDocumentedException.class)
+    @Test
     public void testSameAttrDifferentNamespacesList() throws Exception {
         try {
             edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml");
+            fail();
         } catch (NetconfDocumentedException e) {
             String message = e.getMessage();
-            assertContainsString(message, "Element binaryLeaf present multiple times with different namespaces");
+            assertContainsString(message, "Element allow-user present multiple times with different namespaces");
             assertContainsString(message, TEST_NAMESPACE);
             assertContainsString(message, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
-            throw e;
         }
     }
 
@@ -491,6 +495,7 @@ public class NetconfMappingTest extends AbstractConfigTest {
 
         for (int i = 0; i < TESTS_COUNT; i++) {
             String file = String.format(format, i + 1);
+            logger.info("Reading {}", file);
             try {
                 edit(file);
             } catch (NetconfDocumentedException e) {
@@ -564,23 +569,13 @@ public class NetconfMappingTest extends AbstractConfigTest {
         assertThat(string, JUnitMatchers.containsString(substring));
     }
 
-    private void checkEnum(final Document response) throws NetconfDocumentedException {
-        XmlElement modulesElement = XmlElement.fromDomElement(response.getDocumentElement()).getOnlyChildElement("data")
-                .getOnlyChildElement("modules");
+    private void checkEnum(final Document response) throws Exception {
 
-        String enumName = "extended-enum";
-        String enumContent = "TWO";
+        String expectedEnumContent = "TWO";
 
-        for (XmlElement moduleElement : modulesElement.getChildElements("module")) {
-            String name = moduleElement.getOnlyChildElement("name").getTextContent();
-            if(name.equals(INSTANCE_NAME)) {
-                XmlElement enumAttr = moduleElement.getOnlyChildElement(enumName);
-                assertEquals(enumContent, enumAttr.getTextContent());
-                return;
-            }
-        }
-
-        fail("Enum attribute " + enumName + ":" + enumContent + " not present in " + XmlUtil.toString(response));
+        XMLAssert.assertXpathEvaluatesTo(expectedEnumContent,
+                getXpathForNetconfImplSubnode(INSTANCE_NAME,"extended-enum"),
+                response);
     }
 
     private void checkTestingDeps(Document response) {
@@ -588,24 +583,23 @@ public class NetconfMappingTest extends AbstractConfigTest {
         assertEquals(2, testingDepsSize);
     }
 
-    private void checkTypeConfigAttribute(Document response) throws NetconfDocumentedException {
-
-        XmlElement modulesElement = XmlElement.fromDomElement(response.getDocumentElement()).getOnlyChildElement("data")
-                .getOnlyChildElement("modules");
-
-        List<String> expectedValues = Lists.newArrayList("default-string", "configAttributeType");
-        Set<String> configAttributeType = Sets.newHashSet();
+    private String getXpathForNetconfImplSubnode(String instanceName, String subnode) {
+        return "/urn:ietf:params:xml:ns:netconf:base:1.0:rpc-reply" +
+                "/urn:ietf:params:xml:ns:netconf:base:1.0:data" +
+                "/urn:opendaylight:params:xml:ns:yang:controller:config:modules" +
+                "/module[name='"+instanceName+"']" +
+                "/urn:opendaylight:params:xml:ns:yang:controller:test:impl:impl-netconf" +
+                "/urn:opendaylight:params:xml:ns:yang:controller:test:impl:"+subnode;
+    }
 
-        for (XmlElement moduleElement : modulesElement.getChildElements("module")) {
-            for (XmlElement type : moduleElement.getChildElements("type")) {
-                if (type.getNamespaceOptionally().isPresent()) {
-                    configAttributeType.add(type.getTextContent());
-                }
-            }
-        }
+    private void checkTypeConfigAttribute(Document response) throws Exception {
 
-        for (String expectedValue : expectedValues) {
-            assertTrue(configAttributeType.contains(expectedValue));
+        Map<String,String> namesToTypeValues = ImmutableMap.of("instance-from-code", "configAttributeType",
+                "test2", "default-string");
+        for (Entry<String, String> nameToExpectedValue : namesToTypeValues.entrySet()) {
+            XMLAssert.assertXpathEvaluatesTo(nameToExpectedValue.getValue(),
+                    getXpathForNetconfImplSubnode(nameToExpectedValue.getKey(),"type"),
+                    response);
         }
     }
 
index 57bd3dd6209d3248dc7c7b969ccaf6f5a33fbbf7..1ecbc55b2a02ac7af0757e777d960956cfddc0a0 100644 (file)
@@ -62,6 +62,14 @@ public class NetconfDocumentedException extends Exception {
     private final ErrorSeverity errorSeverity;
     private final Map<String, String> errorInfo;
 
+    public NetconfDocumentedException(String message) {
+        this(message,
+                NetconfDocumentedException.ErrorType.application,
+                NetconfDocumentedException.ErrorTag.invalid_value,
+                NetconfDocumentedException.ErrorSeverity.error
+        );
+    }
+
     public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
             final ErrorSeverity errorSeverity) {
         this(message, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
index 303047df12b6fae6c77e964b702cb23a9ed52b59..7130dc350134578372348ce829bda08243a5303c 100644 (file)
@@ -7,8 +7,12 @@
  */
 package org.opendaylight.controller.netconf.impl.osgi;
 
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.util.HashedWheelTimer;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.TimeUnit;
+
 import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
 import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
@@ -22,11 +26,8 @@ import org.osgi.framework.ServiceRegistration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.concurrent.TimeUnit;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
 
 public class NetconfImplActivator implements BundleActivator {
 
@@ -39,17 +40,16 @@ public class NetconfImplActivator implements BundleActivator {
     private ServiceRegistration<NetconfMonitoringService> regMonitoring;
 
     @Override
-    public void start(final BundleContext context)  {
-        InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
-                "TCP is not configured, netconf not available.", false);
-
-        NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+    public void start(final BundleContext context) {
+        final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfServerAddress(context,
+                NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS);
+        final NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
         startOperationServiceFactoryTracker(context, factoriesListener);
 
-        SessionIdProvider idProvider = new SessionIdProvider();
+        final SessionIdProvider idProvider = new SessionIdProvider();
         timer = new HashedWheelTimer();
-        long connectionTimeoutMillis = NetconfConfigUtil.extractTimeoutMillis(context);
 
+        long connectionTimeoutMillis = NetconfConfigUtil.extractTimeoutMillis(context);
 
         commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
 
@@ -62,23 +62,24 @@ public class NetconfImplActivator implements BundleActivator {
 
         NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
                 serverNegotiatorFactory);
-        NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
+
+        NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, eventLoopGroup,
+                eventLoopGroup);
 
         logger.info("Starting TCP netconf server at {}", address);
         dispatch.createServer(address);
 
         context.registerService(NetconfOperationProvider.class, factoriesListener, null);
-
     }
 
-    private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+    private void startOperationServiceFactoryTracker(final BundleContext context, final NetconfOperationServiceFactoryListenerImpl factoriesListener) {
         factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
         factoriesTracker.open();
     }
 
-    private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
-        NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
-        Dictionary<String, ?> dic = new Hashtable<>();
+    private NetconfMonitoringServiceImpl startMonitoringService(final BundleContext context, final NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+        final NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
+        final Dictionary<String, ?> dic = new Hashtable<>();
         regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic);
 
         return netconfMonitoringServiceImpl;
index 73886c4f461eb3ecd60710da40cf6fc3ff361e54..348fe006f3a7d4cfefcb8c502a9115da422a0a61 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.netconf.ssh.authentication;
 
+import java.security.NoSuchAlgorithmException;
 import org.apache.commons.io.FileUtils;
 import org.bouncycastle.openssl.PEMWriter;
 import org.slf4j.Logger;
@@ -25,7 +26,7 @@ public class PEMGenerator {
     private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
     private static final int KEY_SIZE = 4096;
 
-    public static String generateTo(File privateFile) throws Exception {
+    public static String generateTo(File privateFile) throws IOException, NoSuchAlgorithmException {
         KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
         SecureRandom sr = new SecureRandom();
         keyGen.initialize(KEY_SIZE, sr);
index ca0c9454d4a5c8934a96da245b41029198e464d6..d74308cfadbae8e658e58f9b189d93baaea83c2e 100644 (file)
@@ -7,22 +7,19 @@
  */
 package org.opendaylight.controller.netconf.ssh.osgi;
 
-import com.google.common.base.Optional;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.List;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
 import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
-import org.opendaylight.controller.sal.authorization.UserLevel;
 import org.opendaylight.controller.usermanager.IUserManager;
-import org.opendaylight.controller.usermanager.UserConfig;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -30,7 +27,6 @@ import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
@@ -46,31 +42,28 @@ public class NetconfSSHActivator implements BundleActivator{
 
     private NetconfSSHServer server;
     private static final Logger logger =  LoggerFactory.getLogger(NetconfSSHActivator.class);
-    private static final String EXCEPTION_MESSAGE = "Netconf ssh bridge is not available.";
     private IUserManager iUserManager;
     private BundleContext context = null;
-    private Optional<String> defaultPassword;
-    private Optional<String> defaultUser;
 
     private ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>(){
         @Override
-        public IUserManager addingService(ServiceReference<IUserManager> reference) {
+        public IUserManager addingService(final ServiceReference<IUserManager> reference) {
             logger.trace("Service {} added, let there be SSH bridge.", reference);
             iUserManager =  context.getService(reference);
             try {
                 onUserManagerFound(iUserManager);
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 logger.trace("Can't start SSH server due to {}",e);
             }
             return iUserManager;
         }
         @Override
-        public void modifiedService(ServiceReference<IUserManager> reference, IUserManager service) {
+        public void modifiedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
             logger.trace("Replacing modified service {} in netconf SSH.", reference);
             server.addUserManagerService(service);
         }
         @Override
-        public void removedService(ServiceReference<IUserManager> reference, IUserManager service) {
+        public void removedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
             logger.trace("Removing service {} from netconf SSH. " +
                     "SSH won't authenticate users until IUserManager service will be started.", reference);
             removeUserManagerService();
@@ -79,80 +72,61 @@ public class NetconfSSHActivator implements BundleActivator{
 
 
     @Override
-    public void start(BundleContext context)  {
+    public void start(final BundleContext context) {
         this.context = context;
         listenForManagerService();
     }
 
     @Override
     public void stop(BundleContext context) throws IOException {
-        if (this.defaultUser.isPresent()){
-            this.iUserManager.removeLocalUser(this.defaultUser.get());
-        }
         if (server != null){
             server.stop();
             logger.trace("Netconf SSH bridge is down ...");
         }
     }
-    private void startSSHServer() throws IllegalStateException, IOException {
+    private void startSSHServer() throws IOException {
         checkNotNull(this.iUserManager, "No user manager service available.");
         logger.trace("Starting netconf SSH  bridge.");
-        Optional<InetSocketAddress> sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context, EXCEPTION_MESSAGE);
-        InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfAddress(context,
-                EXCEPTION_MESSAGE, true);
+        final InetSocketAddress sshSocketAddress = NetconfConfigUtil.extractSSHNetconfAddress(context,
+                NetconfConfigUtil.DEFAULT_NETCONF_SSH_ADDRESS);
+        final InetSocketAddress tcpSocketAddress = NetconfConfigUtil.extractTCPNetconfClientAddress(context,
+               NetconfConfigUtil.DEFAULT_NETCONF_TCP_ADDRESS);
 
-        if (sshSocketAddressOptional.isPresent()){
-            String path =  FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(context));
-            if (path.equals("")){
-                throw new IllegalStateException("Missing netconf.ssh.pk.path key in configuration file.");
-            }
+        String path =  FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(context));
 
-            File privateKeyFile = new File(path);
-            String privateKeyPEMString = null;
-            if (privateKeyFile.exists() == false) {
-                try {
-                    privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
-                } catch (Exception e) {
-                    logger.error("Exception occurred while generating PEM string {}",e);
-                }
-            } else {
-                // read from file
-                try (FileInputStream fis = new FileInputStream(path)) {
-                    privateKeyPEMString = IOUtils.toString(fis);
-                } catch (IOException e) {
-                    logger.error("Error reading RSA key from file '{}'", path);
-                    throw new IllegalStateException("Error reading RSA key from file " + path);
-                }
-            }
-            AuthProvider authProvider = null;
+        if (path.isEmpty()) {
+            throw new IllegalStateException("Missing netconf.ssh.pk.path key in configuration file.");
+        }
+
+        final File privateKeyFile = new File(path);
+        final String privateKeyPEMString;
+        if (privateKeyFile.exists() == false) {
+            // generate & save to file
             try {
-                this.defaultPassword = NetconfConfigUtil.getSSHDefaultPassword(context);
-                this.defaultUser = NetconfConfigUtil.getSSHDefaultUser(context);
-                // Since there is no user data store yet (ldap, ...) this adds default user/password to UserManager
-                // if these parameters are set in netconf configuration file.
-                if (defaultUser.isPresent() &&
-                        defaultPassword.isPresent()){
-                    logger.trace(String.format("Default username and password for netconf ssh bridge found. Adding user %s to user manager.",defaultUser.get()));
-                    List<String> roles = new ArrayList<String>(1);
-                    roles.add(UserLevel.SYSTEMADMIN.toString());
-                    iUserManager.addLocalUser(new UserConfig(defaultUser.get(), defaultPassword.get(), roles));
-                }
-                authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
+                privateKeyPEMString = PEMGenerator.generateTo(privateKeyFile);
             } catch (Exception e) {
-                logger.error("Error instantiating AuthProvider {}",e);
+                logger.error("Exception occurred while generating PEM string {}", e);
+                throw new IllegalStateException("Error generating RSA key from file " + path);
             }
-            this.server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddress,authProvider);
-
-            Thread serverThread = new  Thread(server,"netconf SSH server thread");
-            serverThread.setDaemon(true);
-            serverThread.start();
-            logger.trace("Netconf SSH  bridge up and running.");
         } else {
-            logger.trace("No valid connection configuration for SSH bridge found.");
-            throw new IllegalStateException("No valid connection configuration for SSH bridge found.");
+            // read from file
+            try (FileInputStream fis = new FileInputStream(path)) {
+                privateKeyPEMString = IOUtils.toString(fis);
+            } catch (final IOException e) {
+                logger.error("Error reading RSA key from file '{}'", path);
+                throw new IOException("Error reading RSA key from file " + path, e);
+            }
         }
+        final AuthProvider authProvider = new AuthProvider(iUserManager, privateKeyPEMString);
+        this.server = NetconfSSHServer.start(sshSocketAddress.getPort(), tcpSocketAddress, authProvider);
+
+        final Thread serverThread = new Thread(server, "netconf SSH server thread");
+        serverThread.setDaemon(true);
+        serverThread.start();
+        logger.trace("Netconf SSH  bridge up and running.");
     }
-    private void onUserManagerFound(IUserManager userManager) throws IOException {
+
+    private void onUserManagerFound(final IUserManager userManager) throws Exception{
         if (server!=null && server.isUp()){
            server.addUserManagerService(userManager);
         } else {
@@ -163,7 +137,7 @@ public class NetconfSSHActivator implements BundleActivator{
         this.server.removeUserManagerService();
     }
     private void listenForManagerService(){
-        ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(context, IUserManager.class,customizer);
+        final ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(context, IUserManager.class,customizer);
         listenerTracker.open();
     }
 }
index f89df2ac7cc331908f64ed8d05b2dc826f4d7757..0993b8ad0c7038e5b86b0fee0e5cb4d30fd701f8 100644 (file)
@@ -9,19 +9,24 @@
 package org.opendaylight.controller.netconf.util.osgi;
 
 import com.google.common.base.Optional;
-import com.google.common.base.Strings;
-import java.net.InetSocketAddress;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.InetSocketAddress;
 
 public final class NetconfConfigUtil {
     private static final Logger logger = LoggerFactory.getLogger(NetconfConfigUtil.class);
 
+    public static final InetSocketAddress DEFAULT_NETCONF_TCP_ADDRESS
+            = new InetSocketAddress("127.0.0.1", 8383);
+    public static final InetSocketAddress DEFAULT_NETCONF_SSH_ADDRESS
+            = new InetSocketAddress("0.0.0.0", 1830);
+
     private static final String PREFIX_PROP = "netconf.";
 
-    private NetconfConfigUtil() {}
+    private NetconfConfigUtil() {
+    }
 
     private enum InfixProp {
         tcp, ssh
@@ -31,109 +36,133 @@ public final class NetconfConfigUtil {
     private static final String ADDRESS_SUFFIX_PROP = ".address";
     private static final String CLIENT_PROP = ".client";
     private static final String PRIVATE_KEY_PATH_PROP = ".pk.path";
-    private static final String SSH_DEFAULT_USER = ".default.user";
-    private static final String SSH_DEFAULT_PASSWORD = ".default.password";
 
     private static final String CONNECTION_TIMEOUT_MILLIS_PROP = "connectionTimeoutMillis";
     private static final long DEFAULT_TIMEOUT_MILLIS = 5000;
 
-    public static long extractTimeoutMillis(BundleContext bundleContext) {
-        String key = PREFIX_PROP + CONNECTION_TIMEOUT_MILLIS_PROP;
-        String timeoutString = bundleContext.getProperty(key);
+    public static long extractTimeoutMillis(final BundleContext bundleContext) {
+        final String key = PREFIX_PROP + CONNECTION_TIMEOUT_MILLIS_PROP;
+        final String timeoutString = bundleContext.getProperty(key);
         if (timeoutString == null || timeoutString.length() == 0) {
             return DEFAULT_TIMEOUT_MILLIS;
         }
         try {
             return Long.parseLong(timeoutString);
-        }catch(NumberFormatException e) {
+        } catch (final NumberFormatException e) {
             logger.warn("Cannot parse {} property: {}, using defaults", key, timeoutString, e);
             return DEFAULT_TIMEOUT_MILLIS;
         }
     }
 
-    public static InetSocketAddress extractTCPNetconfAddress(BundleContext context, String exceptionMessageIfNotFound, boolean forClient) {
-
-        Optional<InetSocketAddress> inetSocketAddressOptional = extractSomeNetconfAddress(context, InfixProp.tcp, exceptionMessageIfNotFound, forClient);
-
-        if (!inetSocketAddressOptional.isPresent()) {
-            throw new IllegalStateException("Netconf tcp address not found." + exceptionMessageIfNotFound);
-        }
-        InetSocketAddress inetSocketAddress = inetSocketAddressOptional.get();
-        if (inetSocketAddress.getAddress().isAnyLocalAddress()) {
+    public static InetSocketAddress extractTCPNetconfServerAddress(final BundleContext context, final InetSocketAddress defaultAddress) {
+        final Optional<InetSocketAddress> extracted = extractNetconfServerAddress(context, InfixProp.tcp);
+        final InetSocketAddress netconfTcpAddress = getNetconfAddress(defaultAddress, extracted, InfixProp.tcp);
+        logger.debug("Using {} as netconf tcp address", netconfTcpAddress);
+        if (netconfTcpAddress.getAddress().isAnyLocalAddress()) {
             logger.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " +
                     "Consider changing {} to 127.0.0.1", PREFIX_PROP + InfixProp.tcp + ADDRESS_SUFFIX_PROP);
         }
-        return inetSocketAddress;
+        return netconfTcpAddress;
     }
 
-    public static Optional<InetSocketAddress> extractSSHNetconfAddress(BundleContext context, String exceptionMessage) {
-        return extractSomeNetconfAddress(context, InfixProp.ssh, exceptionMessage, false);
+    public static InetSocketAddress extractTCPNetconfClientAddress(final BundleContext context, final InetSocketAddress defaultAddress) {
+        final Optional<InetSocketAddress> extracted = extractNetconfClientAddress(context, InfixProp.tcp);
+        return getNetconfAddress(defaultAddress, extracted, InfixProp.tcp);
     }
 
-    public static String getPrivateKeyPath(BundleContext context){
-        return getPropertyValue(context,PREFIX_PROP + InfixProp.ssh +PRIVATE_KEY_PATH_PROP);
+    /**
+     * Get extracted address or default.
+     *
+     * @throws java.lang.IllegalStateException if neither address is present.
+     */
+    private static InetSocketAddress getNetconfAddress(final InetSocketAddress defaultAddress, Optional<InetSocketAddress> extractedAddress, InfixProp infix) {
+        InetSocketAddress inetSocketAddress;
+
+        if (extractedAddress.isPresent() == false) {
+            logger.debug("Netconf {} address not found, falling back to default {}", infix, defaultAddress);
+
+            if (defaultAddress == null) {
+                logger.warn("Netconf {} address not found, default address not provided", infix);
+                throw new IllegalStateException("Netconf " + infix + " address not found, default address not provided");
+            }
+            inetSocketAddress = defaultAddress;
+        } else {
+            inetSocketAddress = extractedAddress.get();
+        }
+
+        return inetSocketAddress;
     }
-    public static Optional<String> getSSHDefaultUser(BundleContext context){
-        return getOptionalPropertyValue(context,PREFIX_PROP + InfixProp.ssh +SSH_DEFAULT_USER);
+
+    public static InetSocketAddress extractSSHNetconfAddress(final BundleContext context, final InetSocketAddress defaultAddress) {
+        Optional<InetSocketAddress> extractedAddress = extractNetconfServerAddress(context, InfixProp.ssh);
+        InetSocketAddress netconfSSHAddress = getNetconfAddress(defaultAddress, extractedAddress, InfixProp.ssh);
+        logger.debug("Using {} as netconf SSH address", netconfSSHAddress);
+        return netconfSSHAddress;
     }
-    public static Optional<String> getSSHDefaultPassword(BundleContext context){
-        return getOptionalPropertyValue(context,PREFIX_PROP + InfixProp.ssh +SSH_DEFAULT_PASSWORD);
+
+    public static String getPrivateKeyPath(final BundleContext context) {
+        return getPropertyValue(context, PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP);
     }
 
-    private static String getPropertyValue(BundleContext context, String propertyName){
-        String propertyValue = context.getProperty(propertyName);
-        if (propertyValue == null){
-            throw new IllegalStateException("Cannot find initial property with name '"+propertyName+"'");
+    private static String getPropertyValue(final BundleContext context, final String propertyName) {
+        final String propertyValue = context.getProperty(propertyName);
+        if (propertyValue == null) {
+            throw new IllegalStateException("Cannot find initial property with name '" + propertyName + "'");
         }
         return propertyValue;
     }
-    private static Optional<String> getOptionalPropertyValue(BundleContext context, String propertyName){
-        String propertyValue = context.getProperty(propertyName);
-        if (Strings.isNullOrEmpty(propertyValue)){
-            return Optional.absent();
-        }
-        return Optional.fromNullable(propertyValue);
-    }
+
     /**
-     * @param context
-     *            from which properties are being read.
-     * @param infixProp
-     *            either tcp or ssh
-     * @return value if address and port are valid.
-     * @throws IllegalStateException
-     *             if address or port are invalid, or configuration is missing
+     * @param context   from which properties are being read.
+     * @param infixProp either tcp or ssh
+     * @return value if address and port are present and valid, Optional.absent otherwise.
+     * @throws IllegalStateException if address or port are invalid, or configuration is missing
      */
-    private static Optional<InetSocketAddress> extractSomeNetconfAddress(BundleContext context,
-                                                                         InfixProp infixProp,
-                                                                         String exceptionMessage,
-                                                                         boolean client) {
-        String address = "";
-        if (client) {
-            address = context.getProperty(PREFIX_PROP + infixProp + CLIENT_PROP + ADDRESS_SUFFIX_PROP);
-        }
-        if (address == null || address.equals("")){
-            address = context.getProperty(PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP);
-        }
-        if (address == null || address.equals("")) {
-            throw new IllegalStateException("Cannot find initial netconf configuration for parameter    "
-                    +PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP
-                    +" in config.ini. "+exceptionMessage);
-        }
-        String portKey = "";
-        if (client) {
-            portKey = PREFIX_PROP + infixProp + CLIENT_PROP + PORT_SUFFIX_PROP;
+    private static Optional<InetSocketAddress> extractNetconfServerAddress(final BundleContext context,
+                                                                           final InfixProp infixProp) {
+
+        final Optional<String> address = getProperty(context, PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP);
+        final Optional<String> port = getProperty(context, PREFIX_PROP + infixProp + PORT_SUFFIX_PROP);
+
+        if (address.isPresent() && port.isPresent()) {
+            try {
+                return Optional.of(parseAddress(address, port));
+            } catch (final RuntimeException e) {
+                logger.warn("Unable to parse {} netconf address from {}:{}, fallback to default",
+                        infixProp, address, port, e);
+            }
         }
-        if (portKey == null || portKey.equals("")){
-            portKey = PREFIX_PROP + infixProp + PORT_SUFFIX_PROP;
+        return Optional.absent();
+    }
+
+    private static InetSocketAddress parseAddress(final Optional<String> address, final Optional<String> port) {
+        final int portNumber = Integer.valueOf(port.get());
+        return new InetSocketAddress(address.get(), portNumber);
+    }
+
+    private static Optional<InetSocketAddress> extractNetconfClientAddress(final BundleContext context,
+                                                                           final InfixProp infixProp) {
+        final Optional<String> address = getProperty(context,
+                PREFIX_PROP + infixProp + CLIENT_PROP + ADDRESS_SUFFIX_PROP);
+        final Optional<String> port = getProperty(context,
+                PREFIX_PROP + infixProp + CLIENT_PROP + PORT_SUFFIX_PROP);
+
+        if (address.isPresent() && port.isPresent()) {
+            try {
+                return Optional.of(parseAddress(address, port));
+            } catch (final RuntimeException e) {
+                logger.warn("Unable to parse client {} netconf address from {}:{}, fallback to server address",
+                        infixProp, address, port, e);
+            }
         }
-        String portString = context.getProperty(portKey);
-        checkNotNull(portString, "Netconf port must be specified in properties file with " + portKey);
-        try {
-            int port = Integer.valueOf(portString);
-            return Optional.of(new InetSocketAddress(address, port));
-        } catch (RuntimeException e) {
-            throw new IllegalStateException("Cannot create " + infixProp + " netconf address from address:" + address
-                    + " and port:" + portString, e);
+        return extractNetconfServerAddress(context, infixProp);
+    }
+
+    private static Optional<String> getProperty(final BundleContext context, final String propKey) {
+        String value = context.getProperty(propKey);
+        if (value != null && value.isEmpty()) {
+            value = null;
         }
+        return Optional.fromNullable(value);
     }
 }
index e8a453e5d3b9da48431f4886388d89457ff52804..e0d432f27cbb14c581a3bf9fc3453f76d2b2d984 100644 (file)
@@ -16,6 +16,7 @@ import org.custommonkey.xmlunit.AbstractNodeTester;
 import org.custommonkey.xmlunit.NodeTest;
 import org.custommonkey.xmlunit.NodeTestException;
 import org.custommonkey.xmlunit.NodeTester;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -88,7 +89,7 @@ public class XmlUnitUtil {
 
             @Override
             public void noMoreNodes(NodeTest forTest) throws NodeTestException {
-                assertTrue(elementFound);
+                assertTrue(XmlUtil.toString(doc), elementFound);
             }
         };
         assertNodeTestPasses(nt, tester, new short[]{Node.ELEMENT_NODE}, true);
index 35cf2c6a1423e090a3205376a0c2f953c0fd4b48..5dd5a90f7db9550cf4fbe677aea456cdcf821ecc 100644 (file)
                     </type>
 
                     <name>instance-from-code</name>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <sleep-factor>
+                            2.58
+                        </sleep-factor>
 
-                    <sleep-factor>
-                        2.58
-                    </sleep-factor>
+                        <extended>
+                                10
+                        </extended>
 
-                    <extended>
-                            10
-                    </extended>
+                        <extended-twice>
+                                20
+                        </extended-twice>
 
-                    <extended-twice>
-                            20
-                    </extended-twice>
+                        <extended-enum>
+                            TWO
+                        </extended-enum>
 
-                    <extended-enum>
-                        TWO
-                    </extended-enum>
+                        <simple-long-2>44</simple-long-2>
+                        <binaryLeaf>YmluYXJ5</binaryLeaf>
 
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>YmluYXJ5</binaryLeaf>
-
-                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
-                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                        <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                        <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                            <simple-int1>444</simple-int1>
+                            <simple-int2>4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep_user</name>
-                    </testing-dep>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep_user</name>
+                        </testing-dep>
 
-                    <testing-deps>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep_user</name>
-                    </testing-deps>
-                    <testing-deps>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep_user_two</name>
-                    </testing-deps>
+                        <testing-deps>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep_user</name>
+                        </testing-deps>
+                        <testing-deps>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep_user_two</name>
+                        </testing-deps>
+                    </impl-netconf>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <simple-short>4</simple-short>
-
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep_user_two</name>
-                    </testing-dep>
+                    <impl-netconf  xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-short>4</simple-short>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep_user_two</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
index ade40f6a49c94f2a682b7e55e1ec1a3d79e03ecc..be3155222a2564be8a65d6d755672333841ce0dc 100644 (file)
                         test-impl:impl-netconf
                     </type>
                     <name>instance-from-code</name>
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>8ad1</binaryLeaf>
-                    <dto_d>
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-long-2>44</simple-long-2>
+                        <binaryLeaf>8ad1</binaryLeaf>
+                        <dto_d>
+                            <simple-int1>444</simple-int1>
+                            <simple-int2>4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                    </impl-netconf>
                 </module>
                 <module>
                     <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
index 21db4b8e4244b7197f36c8a4761ca4313faf1ba3..3f088e48c9c63e201ff5725442a291f187dc33be 100644 (file)
                     </type>
 
                     <name>instance-from-code</name>
-
-                    <ip xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">127.1.2.3</ip>
-                    <union-test-attr xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">randomStringForUnion</union-test-attr>
-
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <ip xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">127.1.2.3</ip>
+                        <union-test-attr xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">randomStringForUnion</union-test-attr>
+                    </impl-netconf>
                 </module>
             </modules>
 
index df2a5e845257c6b8308ad03eac43a91feb687772..16947f9ec7a3fdcbfa1aff6f23bfd2b1acd1220e 100644 (file)
                     </type>
 
                     <name>test1</name>
-
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>8545649856</binaryLeaf>
-                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
-                    <dto_d>
-                        <simple-int1>444</simple-int1>
-                        <simple-int2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-long-2>44</simple-long-2>
+                        <binaryLeaf>8545649856</binaryLeaf>
+                        <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                        <dto_d>
+                            <simple-int1>444</simple-int1>
+                            <simple-int2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl2">4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
index c399c196d37174a9df5bba448f44d4b7fbdd441d..3dbaef4042a304cc0f03ec67de7cdb2b009442c1 100644 (file)
                     </type>
                 </module>
 
+              <module>
+                <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                  test-impl:impl
+                </type>
+
+                <name>testimpl</name>
+                <simpleInt>1</simpleInt>
+                <simpleInt xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">2</simpleInt>
+              </module>
+
                 <module>
                     <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
                         test-impl:impl-netconf
                     </type>
 
                     <name>test1</name>
-
-                    <simple-long-2>44</simple-long-2>
-                    <simple-long-2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">44</simple-long-2>
-                    <binaryLeaf>8</binaryLeaf>
-                    <binaryLeaf>1</binaryLeaf>
-                    <binaryLeaf>0</binaryLeaf>
-                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
-                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-long-2>44</simple-long-2>
+                        <simple-long-2 xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">44</simple-long-2>
+                        <binaryLeaf></binaryLeaf>
+                        <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
+                        <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                            <simple-int1>444</simple-int1>
+                            <simple-int2>4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
index 47b82c6114f6048fa4a42fd4a8482ac8665162b0..6ee97ee09c624ad7d5dded223f212bc5706f58e2 100644 (file)
 
                 <module>
                     <type xmlns:test-impl="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
-                        test-impl:impl-netconf
+                        test-impl:impl
                     </type>
 
                     <name>test1</name>
-
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>8</binaryLeaf>
-                    <binaryLeaf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">1</binaryLeaf>
-                    <binaryLeaf>0</binaryLeaf>
-                    <type xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">configAttributeType</type>
-                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
-                            <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <allow-user>1</allow-user>
+                    <allow-user xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">2</allow-user>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-                <service>
-                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                    <instance>
-                        <name>ref_dep</name>
-                        <provider>/modules/module[type='impl-dep'][name='dep']
-                        </provider>
-                    </instance>
-                    <instance>
-                        <name>ref_dep_2</name>
-                        <provider>/modules/module[type='impl-dep'][name='dep2']
-                        </provider>
-                    </instance>
-                    <instance>
-                        <name>ref_test1</name>
-                        <provider>
-                            /modules/module[type='impl-netconf'][name='test1']
-                        </provider>
-                    </instance>
-                </service>
+
             </services>
         </config>
     </edit-config>
index 02aca8d787863a2683db402b0c3a9a82ed6ff699..1e5c6da79c1263c3c7591c21dfe261132ab28764 100644 (file)
                     </type>
 
                     <name>test1</name>
-
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>8545649856</binaryLeaf>
-                    <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-long-2>44</simple-long-2>
+                        <binaryLeaf>8545649856</binaryLeaf>
+                        <dto_d xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                            <simple-int1>444</simple-int1>
+                            <simple-int2>4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                      <testing-dep>
+                          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                          <name>ref_dep</name>
+                      </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
index 825be6d19fff18b2d8ccb0c078e6266e4e89c3f1..156c1114081c1c1ba1116beaa7f8b2b2fafa1b69 100644 (file)
                     </type>
 
                     <name>test1</name>
-
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>8545649856</binaryLeaf>
-                    <dto_d>
-                        <unknownAttribute>error</unknownAttribute>
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-long-2>44</simple-long-2>
+                        <binaryLeaf>8545649856</binaryLeaf>
+                        <dto_d>
+                            <unknownAttribute>error</unknownAttribute>
+                            <simple-int1>444</simple-int1>
+                            <simple-int2>4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
index 9ef2bed7d70dfb7654df642480cacc3df354baae..9b9157e65abb0217e7d8b60f78cfe8d670b227aa 100644 (file)
                     </type>
 
                     <name>test1</name>
-
-                    <simple-long-2>44</simple-long-2>
-                    <binaryLeaf>8545649856</binaryLeaf>
-                    <dto_d>
-                        <simple-int1>444</simple-int1>
-                        <simple-int2>4444</simple-int2>
-                        <simple-int3>454</simple-int3>
-                        <complex-dto-bInner>
-                            <simple-int3>44</simple-int3>
-                            <deep>
-                                <simple-int3>4</simple-int3>
-                            </deep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <simple-long-2>44</simple-long-2>
+                        <binaryLeaf>8545649856</binaryLeaf>
+                        <dto_d>
+                            <simple-int1>444</simple-int1>
+                            <simple-int2>4444</simple-int2>
+                            <simple-int3>454</simple-int3>
+                            <complex-dto-bInner>
+                                <simple-int3>44</simple-int3>
+                                <deep>
+                                    <simple-int3>4</simple-int3>
+                                </deep>
+                                <simple-list>4</simple-list>
+                            </complex-dto-bInner>
                             <simple-list>4</simple-list>
-                        </complex-dto-bInner>
-                        <simple-list>4</simple-list>
-                    </dto_d>
-                    <simpleInt>44</simpleInt>
-                    <simple-test>545</simple-test>
-                    <simple-long>454545</simple-long>
-                    <simpleBoolean>false</simpleBoolean>
-                    <dto-c>
-                        <dto-a-inner>
-                            <dto-a-inner-inner>
-                                <simple-arg>456</simple-arg>
-                            </dto-a-inner-inner>
-                            <simple-arg>44</simple-arg>
-                        </dto-a-inner>
-                    </dto-c>
-                    <simple-short>4</simple-short>
-                    <simple-BigInteger>999</simple-BigInteger>
-                    <simple-byte>4</simple-byte>
-                    <peers>
-                        <port>port1</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                    </peers>
-                    <peers>
-                        <port>port23</port>
-                        <simple-int3>456</simple-int3>
-                        <core-size>44</core-size>
-                        <unknownAttribute>error</unknownAttribute>
-                    </peers>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                        </dto_d>
+                        <simpleInt>44</simpleInt>
+                        <simple-test>545</simple-test>
+                        <simple-long>454545</simple-long>
+                        <simpleBoolean>false</simpleBoolean>
+                        <dto-c>
+                            <dto-a-inner>
+                                <dto-a-inner-inner>
+                                    <simple-arg>456</simple-arg>
+                                </dto-a-inner-inner>
+                                <simple-arg>44</simple-arg>
+                            </dto-a-inner>
+                        </dto-c>
+                        <simple-short>4</simple-short>
+                        <simple-BigInteger>999</simple-BigInteger>
+                        <simple-byte>4</simple-byte>
+                        <peers>
+                            <port>port1</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                        </peers>
+                        <peers>
+                            <port>port23</port>
+                            <simple-int3>456</simple-int3>
+                            <core-size>44</core-size>
+                            <unknownAttribute>error</unknownAttribute>
+                        </peers>
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
 
                 <module>
                         test-impl:impl-netconf
                     </type>
                     <name>test2</name>
-                    <testing-dep>
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
-                        <name>ref_dep</name>
-                    </testing-dep>
+                    <impl-netconf xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+                        <testing-dep>
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:test">prefix:testing</type>
+                            <name>ref_dep</name>
+                        </testing-dep>
+                    </impl-netconf>
                 </module>
             </modules>
 
index c30f65903204df027b582c1c5816d5b106c91239..1e2cd47c9c2d923ac09f3cbc50fc60b9c2a519f0 100644 (file)
@@ -22,6 +22,8 @@ import org.opendaylight.controller.networkconfig.neutron.INeutronNetworkCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronRouterCRUD;
 import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityGroupCRUD;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityRuleCRUD;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
 
 public class Activator extends ComponentActivatorAbstractBase {
@@ -64,7 +66,9 @@ public class Activator extends ComponentActivatorAbstractBase {
                 NeutronRouterInterface.class,
                 NeutronPortInterface.class,
                 NeutronSubnetInterface.class,
-                NeutronNetworkInterface.class };
+                NeutronNetworkInterface.class,
+                NeutronSecurityGroupInterface.class,
+                NeutronSecurityRuleInterface.class};
         return res;
     }
 
@@ -163,5 +167,37 @@ public class Activator extends ComponentActivatorAbstractBase {
                     "setConfigurationContainerService",
                     "unsetConfigurationContainerService").setRequired(true));
         }
+        if (imp.equals(NeutronSecurityGroupInterface.class)) {
+            // export the service
+            c.setInterface(
+                new String[] { INeutronSecurityGroupCRUD.class.getName(),
+                    IConfigurationContainerAware.class.getName()}, null);
+            Dictionary<String, String> props = new Hashtable<String, String>();
+            props.put("salListenerName", "neutron");
+            c.add(createContainerServiceDependency(containerName)
+                .setService(IClusterContainerServices.class)
+                .setCallbacks("setClusterContainerService",
+                    "unsetClusterContainerService").setRequired(true));
+            c.add(createContainerServiceDependency(containerName).setService(
+                IConfigurationContainerService.class).setCallbacks(
+                "setConfigurationContainerService",
+                "unsetConfigurationContainerService").setRequired(true));
+        }
+        if (imp.equals(NeutronSecurityRuleInterface.class)) {
+            // export the service
+            c.setInterface(
+                new String[] { INeutronSecurityRuleCRUD.class.getName(),
+                    IConfigurationContainerAware.class.getName()}, null);
+            Dictionary<String, String> props = new Hashtable<String, String>();
+            props.put("salListenerName", "neutron");
+            c.add(createContainerServiceDependency(containerName)
+                .setService(IClusterContainerServices.class)
+                .setCallbacks("setClusterContainerService",
+                    "unsetClusterContainerService").setRequired(true));
+            c.add(createContainerServiceDependency(containerName).setService(
+                IConfigurationContainerService.class).setCallbacks(
+                "setConfigurationContainerService",
+                "unsetConfigurationContainerService").setRequired(true));
+        }
     }
 }
diff --git a/opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/NeutronSecurityGroupInterface.java b/opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/NeutronSecurityGroupInterface.java
new file mode 100644 (file)
index 0000000..a991f61
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.implementation;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+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.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.ConfigurationObject;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerService;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityGroupCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronSecurityGroup;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class NeutronSecurityGroupInterface implements INeutronSecurityGroupCRUD, IConfigurationContainerAware, IObjectReader {
+    private static final Logger logger = LoggerFactory.getLogger(NeutronSecurityGroupInterface.class);
+    private static final String FILE_NAME ="neutron.securitygroup.conf";
+    private String containerName = null;
+
+    private IClusterContainerServices clusterContainerService = null;
+    private IConfigurationContainerService configurationService;
+    private ConcurrentMap<String, NeutronSecurityGroup> securityGroupDB;
+
+    // methods needed for creating caches
+    void setClusterContainerService(IClusterContainerServices s) {
+        logger.debug("Cluster Service set");
+        clusterContainerService = s;
+    }
+
+    void unsetClusterContainerService(IClusterContainerServices s) {
+        if (clusterContainerService == s) {
+            logger.debug("Cluster Service removed!");
+            clusterContainerService = null;
+        }
+    }
+
+    public void setConfigurationContainerService(IConfigurationContainerService service) {
+        logger.trace("Configuration service set: {}", service);
+        configurationService = service;
+    }
+
+    public void unsetConfigurationContainerService(IConfigurationContainerService service) {
+        logger.trace("Configuration service removed: {}", service);
+        configurationService = null;
+    }
+
+    private void allocateCache() {
+        if (this.clusterContainerService == null) {
+            logger.error("un-initialized clusterContainerService, can't create cache");
+            return;
+        }
+        logger.debug("Creating Cache for Neutron Security Groups");
+        try {
+            // neutron caches
+            this.clusterContainerService.createCache("neutronSecurityGroups",
+                EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+        } catch (CacheConfigException cce) {
+            logger.error("Cache couldn't be created for Neutron Security Groups -  check cache mode");
+        } catch (CacheExistException cce) {
+            logger.error("Cache for Neutron Security Groups already exists, destroy and recreate");
+        }
+        logger.debug("Cache successfully created for Neutron Security Groups");
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    private void retrieveCache() {
+        if (clusterContainerService == null) {
+            logger.error("un-initialized clusterContainerService, can't retrieve cache");
+            return;
+        }
+
+        logger.debug("Retrieving cache for Neutron Security Groups");
+        securityGroupDB = (ConcurrentMap<String, NeutronSecurityGroup>) clusterContainerService
+            .getCache("neutronSecurityGroups");
+        if (securityGroupDB == null) {
+            logger.error("Cache couldn't be retrieved for Neutron Security Groups");
+        }
+        logger.debug("Cache was successfully retrieved for Neutron Security Groups");
+    }
+
+    private void destroyCache() {
+        if (clusterContainerService == null) {
+            logger.error("un-initialized clusterMger, can't destroy cache");
+            return;
+        }
+        logger.debug("Destroying Cache for Neutron Security Groups");
+        clusterContainerService.destroyCache("neutronSecurityGroups");
+    }
+
+    private void startUp() {
+        allocateCache();
+        retrieveCache();
+        loadConfiguration();
+    }
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     *
+     */
+    void init(Component c) {
+        Dictionary<?, ?> props = c.getServiceProperties();
+        if (props != null) {
+            this.containerName = (String) props.get("containerName");
+            logger.debug("Running containerName: {}", this.containerName);
+        } else {
+            // In the Global instance case the containerName is empty
+            this.containerName = "";
+        }
+        startUp();
+    }
+
+    /**
+     * Function called by the dependency manager when at least one dependency
+     * become unsatisfied or when the component is shutting down because for
+     * example bundle is being stopped.
+     *
+     */
+    void destroy() {
+        destroyCache();
+    }
+
+    /**
+     * Function called by dependency manager after "init ()" is called and after
+     * the services provided by the class are registered in the service registry
+     *
+     */
+    void start() {
+    }
+
+    /**
+     * Function called by the dependency manager before the services exported by
+     * the component are unregistered, this will be followed by a "destroy ()"
+     * calls
+     *
+     */
+    void stop() {
+    }
+
+    // this method uses reflection to update an object from it's delta.
+
+    private boolean overwrite(Object target, Object delta) {
+        Method[] methods = target.getClass().getMethods();
+
+        for(Method toMethod: methods){
+            if(toMethod.getDeclaringClass().equals(target.getClass())
+                && toMethod.getName().startsWith("set")){
+
+                String toName = toMethod.getName();
+                String fromName = toName.replace("set", "get");
+
+                try {
+                    Method fromMethod = delta.getClass().getMethod(fromName);
+                    Object value = fromMethod.invoke(delta, (Object[])null);
+                    if(value != null){
+                        toMethod.invoke(target, value);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean neutronSecurityGroupExists(String uuid) {
+        return securityGroupDB.containsKey(uuid);
+    }
+
+    @Override
+    public NeutronSecurityGroup getNeutronSecurityGroup(String uuid) {
+        if (!neutronSecurityGroupExists(uuid)) {
+            logger.debug("No Security Groups Have Been Defined");
+            return null;
+        }
+        return securityGroupDB.get(uuid);
+    }
+
+    @Override
+    public List<NeutronSecurityGroup> getAllNeutronSecurityGroups() {
+        Set<NeutronSecurityGroup> allSecurityGroups = new HashSet<NeutronSecurityGroup>();
+        for (Entry<String, NeutronSecurityGroup> entry : securityGroupDB.entrySet()) {
+            NeutronSecurityGroup securityGroup = entry.getValue();
+            allSecurityGroups.add(securityGroup);
+        }
+        logger.debug("Exiting getSecurityGroups, Found {} OpenStackSecurityGroup", allSecurityGroups.size());
+        List<NeutronSecurityGroup> ans = new ArrayList<NeutronSecurityGroup>();
+        ans.addAll(allSecurityGroups);
+        return ans;
+    }
+
+    @Override
+    public boolean addNeutronSecurityGroup(NeutronSecurityGroup input) {
+        if (neutronSecurityGroupExists(input.getSecurityGroupUUID())) {
+            return false;
+        }
+        securityGroupDB.putIfAbsent(input.getSecurityGroupUUID(), input);
+        return true;
+    }
+
+    @Override
+    public boolean removeNeutronSecurityGroup(String uuid) {
+        if (!neutronSecurityGroupExists(uuid)) {
+            return false;
+        }
+        securityGroupDB.remove(uuid);
+        return true;
+    }
+
+    @Override
+    public boolean updateNeutronSecurityGroup(String uuid, NeutronSecurityGroup delta) {
+        if (!neutronSecurityGroupExists(uuid)) {
+            return false;
+        }
+        NeutronSecurityGroup target = securityGroupDB.get(uuid);
+        return overwrite(target, delta);
+    }
+
+    @Override
+    public boolean neutronSecurityGroupInUse(String securityGroupUUID) {
+        return !neutronSecurityGroupExists(securityGroupUUID);
+    }
+
+    private void loadConfiguration() {
+        for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, FILE_NAME)) {
+            NeutronSecurityGroup nn = (NeutronSecurityGroup) conf;
+            securityGroupDB.put(nn.getSecurityGroupUUID(), nn);
+        }
+    }
+
+    @Override
+    public Status saveConfiguration() {
+        return configurationService.persistConfiguration(new ArrayList<ConfigurationObject>(securityGroupDB.values()),
+            FILE_NAME);
+    }
+
+    @Override
+    public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
+        return ois.readObject();
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/NeutronSecurityRuleInterface.java b/opendaylight/networkconfiguration/neutron/implementation/src/main/java/org/opendaylight/controller/networkconfig/neutron/implementation/NeutronSecurityRuleInterface.java
new file mode 100644 (file)
index 0000000..5ca907b
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.implementation;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+
+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.IClusterContainerServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.ConfigurationObject;
+import org.opendaylight.controller.configuration.IConfigurationContainerAware;
+import org.opendaylight.controller.configuration.IConfigurationContainerService;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityRuleCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronSecurityRule;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.Status;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class NeutronSecurityRuleInterface implements INeutronSecurityRuleCRUD, IConfigurationContainerAware, IObjectReader {
+    private static final Logger logger = LoggerFactory.getLogger(NeutronSecurityRuleInterface.class);
+    private static final String FILE_NAME = "neutron.securityrule.conf";
+    private String containerName = null;
+
+    private IClusterContainerServices clusterContainerService = null;
+    private IConfigurationContainerService configurationService;
+    private ConcurrentMap<String, NeutronSecurityRule> securityRuleDB;
+
+    // methods needed for creating caches
+    void setClusterContainerService(IClusterContainerServices s) {
+        logger.debug("Cluster Service set");
+        clusterContainerService = s;
+    }
+
+    void unsetClusterContainerService(IClusterContainerServices s) {
+        if (clusterContainerService == s) {
+            logger.debug("Cluster Service removed!");
+            clusterContainerService = null;
+        }
+    }
+
+    public void setConfigurationContainerService(IConfigurationContainerService service) {
+        logger.trace("Configuration service set: {}", service);
+        configurationService = service;
+    }
+
+    public void unsetConfigurationContainerService(IConfigurationContainerService service) {
+        logger.trace("Configuration service removed: {}", service);
+        configurationService = null;
+    }
+
+    private void allocateCache() {
+        if (this.clusterContainerService == null) {
+            logger.error("un-initialized clusterContainerService, can't create cache");
+            return;
+        }
+        logger.debug("Creating Cache for Neutron Security Rules");
+        try {
+            // neutron caches
+            this.clusterContainerService.createCache("neutronSecurityRules",
+                EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+        } catch (CacheConfigException cce) {
+            logger.error("Cache couldn't be created for Neutron Security Rules -  check cache mode");
+        } catch (CacheExistException cce) {
+            logger.error("Cache for Neutron Security Rules already exists, destroy and recreate");
+        }
+        logger.debug("Cache successfully created for Neutron Security Rules");
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private void retrieveCache() {
+        if (clusterContainerService == null) {
+            logger.error("un-initialized clusterContainerService, can't retrieve cache");
+            return;
+        }
+
+        logger.debug("Retrieving cache for Neutron Security Rules");
+        securityRuleDB = (ConcurrentMap<String, NeutronSecurityRule>) clusterContainerService
+            .getCache("neutronSecurityRules");
+        if (securityRuleDB == null) {
+            logger.error("Cache couldn't be retrieved for Neutron Security Rules");
+        }
+        logger.debug("Cache was successfully retrieved for Neutron Security Rules");
+    }
+
+    private void destroyCache() {
+        if (clusterContainerService == null) {
+            logger.error("un-initialized clusterMger, can't destroy cache");
+            return;
+        }
+        logger.debug("Destroying Cache for Neutron Security Rules");
+        clusterContainerService.destroyCache("neutronSecurityRules");
+    }
+
+    private void startUp() {
+        allocateCache();
+        retrieveCache();
+        loadConfiguration();
+    }
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     */
+    void init(Component c) {
+        Dictionary<?, ?> props = c.getServiceProperties();
+        if (props != null) {
+            this.containerName = (String) props.get("containerName");
+            logger.debug("Running containerName: {}", this.containerName);
+        } else {
+            // In the Global instance case the containerName is empty
+            this.containerName = "";
+        }
+        startUp();
+    }
+
+    /**
+     * Function called by the dependency manager when at least one dependency
+     * become unsatisfied or when the component is shutting down because for
+     * example bundle is being stopped.
+     */
+    void destroy() {
+        destroyCache();
+    }
+
+    /**
+     * Function called by dependency manager after "init ()" is called and after
+     * the services provided by the class are registered in the service registry
+     */
+    void start() {
+    }
+
+    /**
+     * Function called by the dependency manager before the services exported by
+     * the component are unregistered, this will be followed by a "destroy ()"
+     * calls
+     */
+    void stop() {
+    }
+
+    // this method uses reflection to update an object from it's delta.
+    private boolean overwrite(Object target, Object delta) {
+        Method[] methods = target.getClass().getMethods();
+
+        for (Method toMethod : methods) {
+            if (toMethod.getDeclaringClass().equals(target.getClass())
+                && toMethod.getName().startsWith("set")) {
+
+                String toName = toMethod.getName();
+                String fromName = toName.replace("set", "get");
+
+                try {
+                    Method fromMethod = delta.getClass().getMethod(fromName);
+                    Object value = fromMethod.invoke(delta, (Object[]) null);
+                    if (value != null) {
+                        toMethod.invoke(target, value);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean neutronSecurityRuleExists(String uuid) {
+        return securityRuleDB.containsKey(uuid);
+    }
+
+    @Override
+    public NeutronSecurityRule getNeutronSecurityRule(String uuid) {
+        if (!neutronSecurityRuleExists(uuid)) {
+            logger.debug("No Security Rules Have Been Defined");
+            return null;
+        }
+        return securityRuleDB.get(uuid);
+    }
+
+    @Override
+    public List<NeutronSecurityRule> getAllNeutronSecurityRules() {
+        Set<NeutronSecurityRule> allSecurityRules = new HashSet<NeutronSecurityRule>();
+        for (Entry<String, NeutronSecurityRule> entry : securityRuleDB.entrySet()) {
+            NeutronSecurityRule securityRule = entry.getValue();
+            allSecurityRules.add(securityRule);
+        }
+        logger.debug("Exiting getSecurityRule, Found {} OpenStackSecurityRule", allSecurityRules.size());
+        List<NeutronSecurityRule> ans = new ArrayList<NeutronSecurityRule>();
+        ans.addAll(allSecurityRules);
+        return ans;
+    }
+
+    @Override
+    public boolean addNeutronSecurityRule(NeutronSecurityRule input) {
+        if (neutronSecurityRuleExists(input.getSecurityRuleUUID())) {
+            return false;
+        }
+        securityRuleDB.putIfAbsent(input.getSecurityRuleUUID(), input);
+        return true;
+    }
+
+    @Override
+    public boolean removeNeutronSecurityRule(String uuid) {
+        if (!neutronSecurityRuleExists(uuid)) {
+            return false;
+        }
+        securityRuleDB.remove(uuid);
+        return true;
+    }
+
+    @Override
+    public boolean updateNeutronSecurityRule(String uuid, NeutronSecurityRule delta) {
+        if (!neutronSecurityRuleExists(uuid)) {
+            return false;
+        }
+        NeutronSecurityRule target = securityRuleDB.get(uuid);
+        return overwrite(target, delta);
+    }
+
+    @Override
+    public boolean neutronSecurityRuleInUse(String securityRuleUUID) {
+        return !neutronSecurityRuleExists(securityRuleUUID);
+    }
+
+    private void loadConfiguration() {
+        for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, FILE_NAME)) {
+            NeutronSecurityRule nn = (NeutronSecurityRule) conf;
+            securityRuleDB.put(nn.getSecurityRuleUUID(), nn);
+        }
+    }
+
+    @Override
+    public Status saveConfiguration() {
+        return configurationService.persistConfiguration(new ArrayList<ConfigurationObject>(securityRuleDB.values()),
+            FILE_NAME);
+    }
+
+    @Override
+    public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
+        return ois.readObject();
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupAware.java
new file mode 100644 (file)
index 0000000..0fdf77f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+/**
+ * This interface defines the methods a service that wishes to be aware of Neutron Security Groups needs to implement
+ */
+
+public interface INeutronSecurityGroupAware {
+
+    /**
+     * Services provide this interface method to indicate if the specified security group can be created
+     *
+     * @param securityGroup instance of proposed new Neutron Security Group object
+     * @return integer
+     * the return value is understood to be a HTTP status code.  A return value outside of 200 through 299
+     * results in the create operation being interrupted and the returned status value reflected in the
+     * HTTP response.
+     */
+    public int canCreateNeutronSecurityGroup(NeutronSecurityGroup securityGroup);
+
+    /**
+     * Services provide this interface method for taking action after a security group has been created
+     *
+     * @param securityGroup instance of new Neutron Security Group object
+     * @return void
+     */
+    public void neutronSecurityGroupCreated(NeutronSecurityGroup securityGroup);
+
+    /**
+     * Services provide this interface method to indicate if the specified security group can be changed using the specified
+     * delta
+     *
+     * @param delta    updates to the security group object using patch semantics
+     * @param original instance of the Neutron Security Group object to be updated
+     * @return integer
+     * the return value is understood to be a HTTP status code.  A return value outside of 200 through 299
+     * results in the update operation being interrupted and the returned status value reflected in the
+     * HTTP response.
+     */
+    public int canUpdateNeutronSecurityGroup(NeutronSecurityGroup delta, NeutronSecurityGroup original);
+
+    /**
+     * Services provide this interface method for taking action after a security group has been updated
+     *
+     * @param securityGroup instance of modified Neutron Security Group object
+     * @return void
+     */
+    public void neutronSecurityGroupUpdated(NeutronSecurityGroup securityGroup);
+
+    /**
+     * Services provide this interface method to indicate if the specified security group can be deleted
+     *
+     * @param securityGroup instance of the Neutron Security Group object to be deleted
+     * @return integer
+     * the return value is understood to be a HTTP status code.  A return value outside of 200 through 299
+     * results in the delete operation being interrupted and the returned status value reflected in the
+     * HTTP response.
+     */
+    public int canDeleteNeutronSecurityGroup(NeutronSecurityGroup securityGroup);
+
+    /**
+     * Services provide this interface method for taking action after a security group has been deleted
+     *
+     * @param securityGroup instance of deleted Neutron Security Group object
+     * @return void
+     */
+    public void neutronSecurityGroupDeleted(NeutronSecurityGroup securityGroup);
+}
diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityGroupCRUD.java
new file mode 100644 (file)
index 0000000..a408ef9
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+import java.util.List;
+
+/**
+ * This interface defines the methods for CRUD of NB OpenStack Security Group objects
+ */
+
+public interface INeutronSecurityGroupCRUD {
+    /**
+     * Applications call this interface method to determine if a particular
+     * Security Group object exists
+     *
+     * @param uuid UUID of the Security Group object
+     * @return boolean
+     */
+
+    public boolean neutronSecurityGroupExists(String uuid);
+
+    /**
+     * Applications call this interface method to return if a particular
+     * Security Group object exists
+     *
+     * @param uuid UUID of the Security Group object
+     * @return {@link org.opendaylight.controller.networkconfig.neutron.NeutronSecurityGroup.OpenStackSecurity Groups}
+     * OpenStack Security Group class
+     */
+
+    public NeutronSecurityGroup getNeutronSecurityGroup(String uuid);
+
+    /**
+     * Applications call this interface method to return all Security Group objects
+     *
+     * @return List of OpenStackSecurity Groups objects
+     */
+
+    public List<NeutronSecurityGroup> getAllNeutronSecurityGroups();
+
+    /**
+     * Applications call this interface method to add a Security Group object to the
+     * concurrent map
+     *
+     * @param input OpenStackSecurity Group object
+     * @return boolean on whether the object was added or not
+     */
+
+    public boolean addNeutronSecurityGroup(NeutronSecurityGroup input);
+
+    /**
+     * Applications call this interface method to remove a Neutron Security Group object to the
+     * concurrent map
+     *
+     * @param uuid identifier for the security group object
+     * @return boolean on whether the object was removed or not
+     */
+
+    public boolean removeNeutronSecurityGroup(String uuid);
+
+    /**
+     * Applications call this interface method to edit a Security Group object
+     *
+     * @param uuid  identifier of the security group object
+     * @param delta OpenStackSecurity Group object containing changes to apply
+     * @return boolean on whether the object was updated or not
+     */
+
+    public boolean updateNeutronSecurityGroup(String uuid, NeutronSecurityGroup delta);
+
+    /**
+     * Applications call this interface method to see if a MAC address is in use
+     *
+     * @param uuid identifier of the security group object
+     * @return boolean on whether the Security Groups is already in use
+     */
+
+    public boolean neutronSecurityGroupInUse(String uuid);
+
+}
diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleAware.java
new file mode 100644 (file)
index 0000000..ff2a1c4
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+/**
+ * This interface defines the methods required to be aware of Neutron Security Rules
+ */
+
+public interface INeutronSecurityRuleAware {
+
+    /**
+     * Services provide this interface method to indicate if the specified security rule can be created
+     *
+     * @param securityRule instance of proposed new Neutron Security Rule object
+     * @return integer
+     * the return value is understood to be a HTTP status code.  A return value outside of 200 through 299
+     * results in the create operation being interrupted and the returned status value reflected in the
+     * HTTP response.
+     */
+    public int canCreateNeutronSecurityRule(NeutronSecurityRule securityRule);
+
+    /**
+     * Services provide this interface method for taking action after a security rule has been created
+     *
+     * @param securityRule instance of new Neutron Security Rule object
+     * @return void
+     */
+    public void neutronSecurityRuleCreated(NeutronSecurityRule securityRule);
+
+    /**
+     * Services provide this interface method to indicate if the specified security rule can be changed using the specified
+     * delta
+     *
+     * @param delta    updates to the security rule object using patch semantics
+     * @param original instance of the Neutron Security Rule object to be updated
+     * @return integer
+     * the return value is understood to be a HTTP status code.  A return value outside of 200 through 299
+     * results in the update operation being interrupted and the returned status value reflected in the
+     * HTTP response.
+     */
+    public int canUpdateNeutronSecurityRule(NeutronSecurityRule delta, NeutronSecurityRule original);
+
+    /**
+     * Services provide this interface method for taking action after a security rule has been updated
+     *
+     * @param securityRule instance of modified Neutron Security Rule object
+     * @return void
+     */
+    public void neutronSecurityRuleUpdated(NeutronSecurityRule securityRule);
+
+    /**
+     * Services provide this interface method to indicate if the specified security rule can be deleted
+     *
+     * @param securityRule instance of the Neutron Security Rule object to be deleted
+     * @return integer
+     * the return value is understood to be a HTTP status code.  A return value outside of 200 through 299
+     * results in the delete operation being interrupted and the returned status value reflected in the
+     * HTTP response.
+     */
+    public int canDeleteNeutronSecurityRule(NeutronSecurityRule securityRule);
+
+    /**
+     * Services provide this interface method for taking action after a security rule has been deleted
+     *
+     * @param securityRule instance of deleted Neutron Security Rule object
+     * @return void
+     */
+    public void neutronSecurityRuleDeleted(NeutronSecurityRule securityRule);
+}
diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/INeutronSecurityRuleCRUD.java
new file mode 100644 (file)
index 0000000..73b41c7
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+import java.util.List;
+
+/**
+ * This interface defines the methods for CRUD of NB OpenStack Security Rule objects
+ */
+
+public interface INeutronSecurityRuleCRUD {
+    /**
+     * Applications call this interface method to determine if a particular
+     * Security Rule object exists
+     *
+     * @param uuid UUID of theSecurity Rule object
+     * @return boolean
+     */
+
+    public boolean neutronSecurityRuleExists(String uuid);
+
+    /**
+     * Applications call this interface method to return if a particular
+     * Security Rule object exists
+     *
+     * @param uuid UUID of the security rule object
+     * @return {@link org.opendaylight.controller.networkconfig.neutron.NeutronSecurityRule.OpenStackNetworks}
+     * OpenStackSecurity Rule class
+     */
+
+    public NeutronSecurityRule getNeutronSecurityRule(String uuid);
+
+    /**
+     * Applications call this interface method to return all Security Rule objects
+     *
+     * @return List of OpenStack SecurityRules objects
+     */
+
+    public List<NeutronSecurityRule> getAllNeutronSecurityRules();
+
+    /**
+     * Applications call this interface method to add a Security Rule object to the
+     * concurrent map
+     *
+     * @param input OpenStack security rule object
+     * @return boolean on whether the object was added or not
+     */
+
+    public boolean addNeutronSecurityRule(NeutronSecurityRule input);
+
+    /**
+     * Applications call this interface method to remove a Neutron Security Rule object to the
+     * concurrent map
+     *
+     * @param uuid identifier for the security rule object
+     * @return boolean on whether the object was removed or not
+     */
+
+    public boolean removeNeutronSecurityRule(String uuid);
+
+    /**
+     * Applications call this interface method to edit aSecurity Rule object
+     *
+     * @param uuid  identifier of the security rule object
+     * @param delta OpenStackSecurity Rule object containing changes to apply
+     * @return boolean on whether the object was updated or not
+     */
+
+    public boolean updateNeutronSecurityRule(String uuid, NeutronSecurityRule delta);
+
+    /**
+     * Applications call this interface method to see if a MAC address is in use
+     *
+     * @param uuid identifier of the security rule object
+     * @return boolean on whether the macAddress is already associated with a
+     * port or not
+     */
+
+    public boolean neutronSecurityRuleInUse(String uuid);
+
+}
index aebecfa93e18884d243cfb58227124a98de6d429..21cfdb1305060d1bfa24ff32866f1ce0aa8509b5 100644 (file)
@@ -36,4 +36,14 @@ public class NeutronCRUDInterfaces {
         INeutronFloatingIPCRUD answer = (INeutronFloatingIPCRUD) ServiceHelper.getGlobalInstance(INeutronFloatingIPCRUD.class, o);
         return answer;
     }
-}
+
+    public static INeutronSecurityGroupCRUD getINeutronSecurityGroupCRUD(Object o) {
+        INeutronSecurityGroupCRUD answer = (INeutronSecurityGroupCRUD) ServiceHelper.getGlobalInstance(INeutronSecurityGroupCRUD.class, o);
+        return answer;
+    }
+
+    public static INeutronSecurityRuleCRUD getINeutronSecurityRuleCRUD(Object o) {
+        INeutronSecurityRuleCRUD answer = (INeutronSecurityRuleCRUD) ServiceHelper.getGlobalInstance(INeutronSecurityRuleCRUD.class, o);
+        return answer;
+    }
+}
\ No newline at end of file
index c8ee4e8cccd3021b3a30deb4eb8ca56b16e17593..680a07453b94b3f56072804b2981006574837f5a 100644 (file)
@@ -61,9 +61,8 @@ public class NeutronPort extends ConfigurationObject implements Serializable {
     @XmlElement (name="tenant_id")
     String tenantID;
 
-    // TODO: add security groups
-    //        @XmlElement (name="security_groups")
-    //        List<String> securityGroups;
+    @XmlElement (name="security_groups")
+    List<NeutronSecurityGroup> securityGroups;
 
     /* this attribute stores the floating IP address assigned to
      * each fixed IP address
@@ -162,6 +161,14 @@ public class NeutronPort extends ConfigurationObject implements Serializable {
         this.tenantID = tenantID;
     }
 
+    public List<NeutronSecurityGroup> getSecurityGroups() {
+        return securityGroups;
+    }
+
+    public void setSecurityGroups(List<NeutronSecurityGroup> securityGroups) {
+        this.securityGroups = securityGroups;
+    }
+
     public NeutronFloatingIP getFloatingIP(String key) {
         if (!floatingIPMap.containsKey(key)) {
             return null;
@@ -259,6 +266,6 @@ public class NeutronPort extends ConfigurationObject implements Serializable {
         return "NeutronPort [portUUID=" + portUUID + ", networkUUID=" + networkUUID + ", name=" + name
                 + ", adminStateUp=" + adminStateUp + ", status=" + status + ", macAddress=" + macAddress
                 + ", fixedIPs=" + fixedIPs + ", deviceID=" + deviceID + ", deviceOwner=" + deviceOwner + ", tenantID="
-                + tenantID + ", floatingIPMap=" + floatingIPMap + "]";
+                + tenantID + ", floatingIPMap=" + floatingIPMap + ", securityGroups=" + securityGroups + "]";
     }
 }
diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSecurityGroup.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSecurityGroup.java
new file mode 100644 (file)
index 0000000..0f0a14c
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+import org.opendaylight.controller.configuration.ConfigurationObject;
+
+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.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * OpenStack Neutron v2.0 Security Group bindings.
+ * See OpenStack Network API v2.0 Reference for description of
+ * annotated attributes. The current fields are as follows:
+ * <p/>
+ * id                   uuid-str unique ID for the security group.
+ * name                 String name of the security group.
+ * description          String name of the security group.
+ * tenant_id            uuid-str Owner of security rule..
+ * security_group_rules List<NeutronSecurityRule> nested RO in the sec group.
+ */
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+
+public class NeutronSecurityGroup extends ConfigurationObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @XmlElement(name = "id")
+    String securityGroupUUID;
+
+    @XmlElement(name = "name")
+    String securityGroupName;
+
+    @XmlElement(name = "description")
+    String securityGroupDescription;
+
+    @XmlElement(name = "tenant_id")
+    String securityGroupTenantID;
+
+    @XmlElement(name = "security_group_rules")
+    List<NeutronSecurityRule> neutronSecurityRule;
+
+    List<NeutronPort> neutronPorts;
+
+    public NeutronSecurityGroup() {
+        neutronPorts = new ArrayList<NeutronPort> ();
+        List<NeutronSecurityRule> securityRules;
+
+    }
+
+    public String getSecurityGroupUUID() {
+        return securityGroupUUID;
+    }
+
+    public void setSecurityGroupUUID(String securityGroupUUID) {
+        this.securityGroupUUID = securityGroupUUID;
+    }
+
+    public String getSecurityGroupName() {
+        return securityGroupName;
+    }
+
+    public void setSecurityGroupName(String securityGroupName) {
+        this.securityGroupName = securityGroupName;
+    }
+
+    public String getSecurityGroupDescription() {
+        return securityGroupDescription;
+    }
+
+    public void setSecurityGroupDescription(String securityGroupDescription) {
+        this.securityGroupDescription = securityGroupDescription;
+    }
+
+    public String getSecurityGroupTenantID() {
+        return securityGroupTenantID;
+    }
+
+    public void setSecurityGroupTenantID(String securityGroupTenantID) {
+        this.securityGroupTenantID = securityGroupTenantID;
+    }
+
+    // Rules In Group
+    public List<NeutronSecurityRule> getSecurityRules() {
+        return neutronSecurityRule;
+    }
+
+    public void setSecurityRules(NeutronSecurityRule neutronSecurityRule) {
+        this.neutronSecurityRule = (List<NeutronSecurityRule>) neutronSecurityRule;
+    }
+
+    public NeutronSecurityGroup extractFields(List<String> fields) {
+        NeutronSecurityGroup ans = new NeutronSecurityGroup ();
+        Iterator<String> i = fields.iterator ();
+        while (i.hasNext ()) {
+            String s = i.next ();
+            if (s.equals ("id")) {
+                ans.setSecurityGroupUUID (this.getSecurityGroupUUID ());
+            }
+            if (s.equals ("name")) {
+                ans.setSecurityGroupName (this.getSecurityGroupName ());
+            }
+            if (s.equals ("description")) {
+                ans.setSecurityGroupDescription (this.getSecurityGroupDescription ());
+            }
+            if (s.equals ("tenant_id")) {
+                ans.setSecurityGroupTenantID (this.getSecurityGroupTenantID ());
+            }
+            if (s.equals ("security_group_rules")) {
+                ans.setSecurityRules ((NeutronSecurityRule) this.getSecurityRules ());
+            }
+        }
+        return ans;
+    }
+
+    @Override
+    public String toString() {
+        return "NeutronSecurityGroup{" +
+                "securityGroupUUID='" + securityGroupUUID + '\'' +
+                ", securityGroupName='" + securityGroupName + '\'' +
+                ", securityGroupDescription='" + securityGroupDescription + '\'' +
+                ", securityGroupTenantID='" + securityGroupTenantID + '\'' +
+                ", securityRules=" + neutronSecurityRule + "]";
+    }
+
+    public void initDefaults() {
+        //TODO verify no defaults values are nessecary required.
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSecurityRule.java b/opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSecurityRule.java
new file mode 100644 (file)
index 0000000..3fad4fe
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron;
+
+import org.opendaylight.controller.configuration.ConfigurationObject;
+
+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.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * See OpenStack Network API v2.0 Reference for description of
+ * annotated attributes. The current fields are as follows:
+ * <p/>
+ * id                uuid (String) UUID for the security group rule.
+ * security_rule_id  uuid (String) The security group to associate rule.
+ * direction         String Direction the VM traffic  (ingress/egress).
+ * security_group_id The security group to associate rule with.
+ * protocol          String IP Protocol (icmp, tcp, udp, etc).
+ * port_range_min    Integer Port at start of range
+ * port_range_max    Integer Port at end of range
+ * ethertype         String ethertype in L2 packet (IPv4, IPv6, etc)
+ * remote_ip_prefix  String (IP cidr) CIDR for address range.
+ * remote_group_id   uuid-str Source security group to apply to rule.
+ * tenant_id         uuid-str Owner of security rule. Admin only outside tenant.
+ */
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+
+public class NeutronSecurityRule extends ConfigurationObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @XmlElement(name = "id")
+    String securityRuleUUID;
+
+    @XmlElement(name = "direction")
+    String securityRuleDirection;
+
+    @XmlElement(name = "protocol")
+    String securityRuleProtocol;
+
+    @XmlElement(name = "port_range_min")
+    Integer securityRulePortMin;
+
+    @XmlElement(name = " port_range_max")
+    Integer securityRulePortMax;
+
+    @XmlElement(name = "ethertype")
+    String securityRuleEthertype;
+
+    @XmlElement(name = "remote_ip_prefix")
+    String securityRuleRemoteIpPrefix;
+
+    @XmlElement(name = "remote_group_id")
+    String securityRemoteGroupID;
+
+    @XmlElement(name = "security_group_id")
+    String securityRuleGroupID;
+
+    @XmlElement(name = "tenant_id")
+    String securityRuleTenantID;
+
+    public NeutronSecurityRule() {
+        List<NeutronSecurityRule> securityRules;
+    }
+
+    public String getSecurityRuleUUID() {
+        return securityRuleUUID;
+    }
+
+    public void setSecurityRuleUUID(String securityRuleUUID) {
+        this.securityRuleUUID = securityRuleUUID;
+    }
+
+    public String getSecurityRuleDirection() {
+        return securityRuleDirection;
+    }
+
+    public void setSecurityRuleDirection(String securityRuleDirection) {
+        this.securityRuleDirection = securityRuleDirection;
+    }
+
+    public String getSecurityRuleProtocol() {
+        return securityRuleProtocol;
+    }
+
+    public void setSecurityRuleProtocol(String securityRuleProtocol) {
+        this.securityRuleProtocol = securityRuleProtocol;
+    }
+
+    public Integer getSecurityRulePortMin() {
+        return securityRulePortMin;
+    }
+
+    public void setSecurityRulePortMin(Integer securityRulePortMin) {
+        this.securityRulePortMin = securityRulePortMin;
+    }
+
+    public Integer getSecurityRulePortMax() {
+        return securityRulePortMax;
+    }
+
+    public void setSecurityRulePortMax(Integer securityRulePortMax) {
+        this.securityRulePortMax = securityRulePortMax;
+    }
+
+    public String getSecurityRuleEthertype() {
+        return securityRuleEthertype;
+    }
+
+    public void setSecurityRuleEthertype(String securityRuleEthertype) {
+        this.securityRuleEthertype = securityRuleEthertype;
+    }
+
+    public String getSecurityRuleRemoteIpPrefix() {
+        return securityRuleRemoteIpPrefix;
+    }
+
+    public void setSecurityRuleRemoteIpPrefix(String securityRuleRemoteIpPrefix) {
+        this.securityRuleRemoteIpPrefix = securityRuleRemoteIpPrefix;
+    }
+
+    public String getSecurityRemoteGroupID() {
+        return securityRemoteGroupID;
+    }
+
+    public void setSecurityRemoteGroupID(String securityRemoteGroupID) {
+        this.securityRemoteGroupID = securityRemoteGroupID;
+    }
+
+    public String getSecurityRuleGroupID() {
+        return securityRuleGroupID;
+    }
+
+    public void setSecurityRuleGroupID(String securityRuleGroupID) {
+        this.securityRuleGroupID = securityRuleGroupID;
+    }
+
+    public String getSecurityRuleTenantID() {
+        return securityRuleTenantID;
+    }
+
+    public void setSecurityRuleTenantID(String securityRuleTenantID) {
+        this.securityRuleTenantID = securityRuleTenantID;
+    }
+
+    public NeutronSecurityRule extractFields(List<String> fields) {
+        NeutronSecurityRule ans = new NeutronSecurityRule();
+        Iterator<String> i = fields.iterator();
+        while (i.hasNext()) {
+            String s = i.next();
+            if (s.equals("id")) {
+                ans.setSecurityRuleUUID(this.getSecurityRuleUUID());
+            }
+            if (s.equals("direction")) {
+                ans.setSecurityRuleDirection(this.getSecurityRuleDirection());
+            }
+            if (s.equals("protocol")) {
+                ans.setSecurityRuleProtocol(this.getSecurityRuleProtocol());
+            }
+            if (s.equals("port_range_min")) {
+                ans.setSecurityRulePortMin(this.getSecurityRulePortMin());
+            }
+            if (s.equals("port_range_max")) {
+                ans.setSecurityRulePortMax(this.getSecurityRulePortMax());
+            }
+            if (s.equals("ethertype")) {
+                ans.setSecurityRuleEthertype(this.getSecurityRuleEthertype());
+            }
+            if (s.equals("remote_ip_prefix")) {
+                ans.setSecurityRuleRemoteIpPrefix(this.getSecurityRuleRemoteIpPrefix());
+            }
+            if (s.equals("remote_group_id")) {
+                ans.setSecurityRemoteGroupID(this.getSecurityRemoteGroupID());
+            }
+            if (s.equals("security_group_id")) {
+                ans.setSecurityRuleGroupID(this.getSecurityRuleGroupID());
+            }
+            if (s.equals("tenant_id")) {
+                ans.setSecurityRuleTenantID(this.getSecurityRuleTenantID());
+            }
+        }
+        return ans;
+    }
+
+    @Override
+    public String toString() {
+        return "NeutronSecurityRule{" +
+            "securityRuleUUID='" + securityRuleUUID + '\'' +
+            ", securityRuleDirection='" + securityRuleDirection + '\'' +
+            ", securityRuleProtocol='" + securityRuleProtocol + '\'' +
+            ", securityRulePortMin=" + securityRulePortMin +
+            ", securityRulePortMax=" + securityRulePortMax +
+            ", securityRuleEthertype='" + securityRuleEthertype + '\'' +
+            ", securityRuleRemoteIpPrefix='" + securityRuleRemoteIpPrefix + '\'' +
+            ", securityRemoteGroupID=" + securityRemoteGroupID +
+            ", securityRuleGroupID='" + securityRuleGroupID + '\'' +
+            ", securityRuleTenantID='" + securityRuleTenantID + '\'' +
+            '}';
+    }
+
+    public void initDefaults() {
+        //TODO verify no defaults values are nessecary required.
+    }
+}
\ No newline at end of file
index 76c39e4294dd05a0ada503edd05d1243c9b436f5..3fe03a2dac8de2f64d9ee2b9796befe72ab0e8b1 100644 (file)
@@ -32,6 +32,8 @@ public class NeutronNorthboundRSApplication extends Application {
         classes.add(NeutronPortsNorthbound.class);
         classes.add(NeutronRoutersNorthbound.class);
         classes.add(NeutronFloatingIPsNorthbound.class);
+        classes.add(NeutronSecurityGroupsNorthbound.class);
+        classes.add(NeutronSecurityRulesNorthbound.class);
         return classes;
     }
 
diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityGroupRequest.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityGroupRequest.java
new file mode 100644 (file)
index 0000000..6e779d6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.northbound;
+
+import org.opendaylight.controller.networkconfig.neutron.NeutronSecurityGroup;
+
+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 NeutronSecurityGroupRequest {
+    /**
+    * See OpenStack Network API v2.0 Reference for a
+    * description of annotated attributes and operations
+    */
+
+    @XmlElement (name = "security_group")
+    NeutronSecurityGroup singletonSecurityGroup;
+
+    @XmlElement (name = "security_groups")
+    List<NeutronSecurityGroup> bulkRequest;
+
+    NeutronSecurityGroupRequest() {
+    }
+
+    NeutronSecurityGroupRequest(List<NeutronSecurityGroup> bulk) {
+        bulkRequest = bulk;
+        singletonSecurityGroup = null;
+    }
+
+    NeutronSecurityGroupRequest(NeutronSecurityGroup group) {
+        singletonSecurityGroup = group;
+    }
+
+    public List<NeutronSecurityGroup> getBulk() {
+        return bulkRequest;
+    }
+
+    public NeutronSecurityGroup getSingleton() {
+        return singletonSecurityGroup;
+    }
+
+    public boolean isSingleton() {
+        return (singletonSecurityGroup != null);
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityGroupsNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityGroupsNorthbound.java
new file mode 100644 (file)
index 0000000..5e9a331
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.northbound;
+
+
+import java.util.ArrayList;
+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.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityGroupAware;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityGroupCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
+import org.opendaylight.controller.networkconfig.neutron.NeutronSecurityGroup;
+import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
+import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Neutron Northbound REST APIs for Security Group.<br>
+ * This class provides REST APIs for managing neutron Security Group
+ * <p/>
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default. Administrator can enable it in
+ * tomcat-server.xml after adding a proper keystore / SSL certificate from a
+ * trusted authority.<br>
+ * More info :
+ * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ */
+@Path ("/security-groups")
+public class NeutronSecurityGroupsNorthbound {
+    static final Logger logger = LoggerFactory.getLogger(NeutronSecurityGroupsNorthbound.class);
+
+    private NeutronSecurityGroup extractFields(NeutronSecurityGroup o, List<String> fields) {
+        return o.extractFields(fields);
+    }
+
+    /**
+     * Returns a list of all Security Groups
+     */
+    @GET
+    @Produces ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 200, condition = "Operation successful"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+
+    public Response listGroups(
+            // return fields
+            @QueryParam ("fields") List<String> fields,
+            // OpenStack security group attributes
+            @QueryParam ("id") String querySecurityGroupUUID,
+            @QueryParam ("name") String querySecurityGroupName,
+            @QueryParam ("description") String querySecurityDescription,
+            @QueryParam ("tenant_id") String querySecurityTenantID,
+            @QueryParam ("limit") String limit,
+            @QueryParam ("marker") String marker,
+            @QueryParam ("page_reverse") String pageReverse
+    ) {
+        INeutronSecurityGroupCRUD securityGroupInterface = NeutronCRUDInterfaces.getINeutronSecurityGroupCRUD(this);
+
+        if (securityGroupInterface == null) {
+            throw new ServiceUnavailableException("Security Group CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+        List<NeutronSecurityGroup> allSecurityGroups = securityGroupInterface.getAllNeutronSecurityGroups();
+        List<NeutronSecurityGroup> ans = new ArrayList<NeutronSecurityGroup>();
+        Iterator<NeutronSecurityGroup> i = allSecurityGroups.iterator();
+        while (i.hasNext()) {
+            NeutronSecurityGroup nsg = i.next();
+            if ((querySecurityGroupUUID == null ||
+                    querySecurityGroupUUID.equals(nsg.getSecurityGroupUUID())) &&
+                    (querySecurityGroupName == null ||
+                            querySecurityGroupName.equals(nsg.getSecurityGroupName())) &&
+                    (querySecurityDescription == null ||
+                            querySecurityDescription.equals(nsg.getSecurityGroupDescription())) &&
+                    (querySecurityTenantID == null ||
+                            querySecurityTenantID.equals(nsg.getSecurityGroupTenantID()))) {
+                if (fields.size() > 0) {
+                    ans.add(extractFields(nsg, fields));
+                } else {
+                    ans.add(nsg);
+                }
+            }
+        }
+        return Response.status(200).entity(
+                new NeutronSecurityGroupRequest(ans)).build();
+    }
+
+    /**
+     * Returns a specific Security Group
+     */
+
+    @Path ("{securityGroupUUID}")
+    @GET
+    @Produces ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 200, condition = "Operation successful"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response showSecurityGroup(@PathParam ("securityGroupUUID") String securityGroupUUID,
+                                      // return fields
+                                      @QueryParam ("fields") List<String> fields) {
+        INeutronSecurityGroupCRUD securityGroupInterface = NeutronCRUDInterfaces.getINeutronSecurityGroupCRUD(this);
+        if (securityGroupInterface == null) {
+            throw new ServiceUnavailableException("Security Group CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+        if (!securityGroupInterface.neutronSecurityGroupExists(securityGroupUUID)) {
+            throw new ResourceNotFoundException("Security Group UUID does not exist.");
+        }
+        if (!fields.isEmpty()) {
+            NeutronSecurityGroup ans = securityGroupInterface.getNeutronSecurityGroup(securityGroupUUID);
+            return Response.status(200).entity(
+                    new NeutronSecurityGroupRequest(extractFields(ans, fields))).build();
+        } else {
+            return Response.status(200).entity(new NeutronSecurityGroupRequest(securityGroupInterface.getNeutronSecurityGroup(securityGroupUUID))).build();
+        }
+    }
+
+    /**
+     * Creates new Security Group
+     */
+
+    @POST
+    @Produces ({MediaType.APPLICATION_JSON})
+    @Consumes ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 201, condition = "Created"),
+            @ResponseCode (code = 400, condition = "Bad Request"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 403, condition = "Forbidden"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 409, condition = "Conflict"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response createSecurityGroups(final NeutronSecurityGroupRequest input) {
+        INeutronSecurityGroupCRUD securityGroupInterface = NeutronCRUDInterfaces.getINeutronSecurityGroupCRUD(this);
+        if (securityGroupInterface == null) {
+            throw new ServiceUnavailableException("Security Group CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        if (input.isSingleton()) {
+            NeutronSecurityGroup singleton = input.getSingleton();
+
+            /*
+             *  Verify that the Security Group doesn't already exist.
+             */
+            if (securityGroupInterface.neutronSecurityGroupExists(singleton.getSecurityGroupUUID())) {
+                throw new BadRequestException("Security Group UUID already exists");
+            }
+
+            Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityGroupAware.class, this, null);
+            if (instances != null) {
+                for (Object instance : instances) {
+                    INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                    int status = service.canCreateNeutronSecurityGroup(singleton);
+                    if (status < 200 || status > 299) {
+                        return Response.status(status).build();
+                    }
+                }
+            }
+            // Add to Neutron cache
+            securityGroupInterface.addNeutronSecurityGroup(singleton);
+            if (instances != null) {
+                for (Object instance : instances) {
+                    INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                    service.neutronSecurityGroupCreated(singleton);
+                }
+            }
+        } else {
+            List<NeutronSecurityGroup> bulk = input.getBulk();
+            Iterator<NeutronSecurityGroup> i = bulk.iterator();
+            HashMap<String, NeutronSecurityGroup> testMap = new HashMap<String, NeutronSecurityGroup>();
+            Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityGroupAware.class, this, null);
+            while (i.hasNext()) {
+                NeutronSecurityGroup test = i.next();
+
+                /*
+                 *  Verify that the security group doesn't already exist
+                 */
+
+                if (securityGroupInterface.neutronSecurityGroupExists(test.getSecurityGroupUUID())) {
+                    throw new BadRequestException("Security Group UUID already is already created");
+                }
+                if (instances != null) for (Object instance : instances) {
+                    INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                    int status = service.canCreateNeutronSecurityGroup(test);
+                    if ((status < 200) || (status > 299)) return Response.status(status).build();
+                }
+            }
+
+            /*
+             * now, each element of the bulk request can be added to the cache
+             */
+            i = bulk.iterator();
+            while (i.hasNext()) {
+                NeutronSecurityGroup test = i.next();
+                securityGroupInterface.addNeutronSecurityGroup(test);
+                if (instances != null) {
+                    for (Object instance : instances) {
+                        INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                        service.neutronSecurityGroupCreated(test);
+                    }
+                }
+            }
+        }
+        return Response.status(201).entity(input).build();
+    }
+
+    /**
+     * Updates a Security Group
+     */
+
+    @Path ("{securityGroupUUID}")
+    @PUT
+    @Produces ({MediaType.APPLICATION_JSON})
+    @Consumes ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 200, condition = "Operation successful"),
+            @ResponseCode (code = 400, condition = "Bad Request"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 403, condition = "Forbidden"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response updateSecurityGroup(
+            @PathParam ("securityGroupUUID") String securityGroupUUID, final NeutronSecurityGroupRequest input) {
+        INeutronSecurityGroupCRUD securityGroupInterface = NeutronCRUDInterfaces.getINeutronSecurityGroupCRUD(this);
+        if (securityGroupInterface == null) {
+            throw new ServiceUnavailableException("Security Group CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        /*
+         * verify the Security Group exists and there is only one delta provided
+         */
+        if (!securityGroupInterface.neutronSecurityGroupExists(securityGroupUUID)) {
+            throw new ResourceNotFoundException("Security Group UUID does not exist.");
+        }
+        if (!input.isSingleton()) {
+            throw new BadRequestException("Only singleton edit supported");
+        }
+        NeutronSecurityGroup delta = input.getSingleton();
+        NeutronSecurityGroup original = securityGroupInterface.getNeutronSecurityGroup(securityGroupUUID);
+
+        if (delta.getSecurityGroupUUID() != null ||
+                delta.getSecurityGroupTenantID() != null ||
+                delta.getSecurityGroupName() != null ||
+                delta.getSecurityGroupDescription() != null) {
+            throw new BadRequestException("Attribute edit blocked by Neutron");
+        }
+
+        Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityGroupAware.class, this, null);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                int status = service.canUpdateNeutronSecurityGroup(delta, original);
+                if (status < 200 || status > 299) {
+                    return Response.status(status).build();
+                }
+            }
+        }
+
+        /*
+         * update the object and return it
+         */
+        securityGroupInterface.updateNeutronSecurityGroup(securityGroupUUID, delta);
+        NeutronSecurityGroup updatedSecurityGroup = securityGroupInterface.getNeutronSecurityGroup(securityGroupUUID);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                service.neutronSecurityGroupUpdated(updatedSecurityGroup);
+            }
+        }
+        return Response.status(200).entity(new NeutronSecurityGroupRequest(securityGroupInterface.getNeutronSecurityGroup(securityGroupUUID))).build();
+    }
+
+    /**
+     * Deletes a Security Group
+     */
+
+    @Path ("{securityGroupUUID}")
+    @DELETE
+    @StatusCodes ({
+            @ResponseCode (code = 204, condition = "No Content"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 409, condition = "Conflict"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response deleteSecurityGroup(
+            @PathParam ("securityGroupUUID") String securityGroupUUID) {
+        INeutronSecurityGroupCRUD securityGroupInterface = NeutronCRUDInterfaces.getINeutronSecurityGroupCRUD(this);
+        if (securityGroupInterface == null) {
+            throw new ServiceUnavailableException("Security Group CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        /*
+         * verify the Security Group exists and it isn't currently in use
+         */
+        if (!securityGroupInterface.neutronSecurityGroupExists(securityGroupUUID)) {
+            throw new ResourceNotFoundException("Security Group UUID does not exist.");
+        }
+        if (securityGroupInterface.neutronSecurityGroupInUse(securityGroupUUID)) {
+            return Response.status(409).build();
+        }
+        NeutronSecurityGroup singleton = securityGroupInterface.getNeutronSecurityGroup(securityGroupUUID);
+        Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityGroupAware.class, this, null);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                int status = service.canDeleteNeutronSecurityGroup(singleton);
+                if ((status < 200) || (status > 299)) {
+                    return Response.status(status).build();
+                }
+            }
+        }
+
+        /*
+         * remove it and return 204 status
+         */
+        securityGroupInterface.removeNeutronSecurityGroup(securityGroupUUID);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityGroupAware service = (INeutronSecurityGroupAware) instance;
+                service.neutronSecurityGroupDeleted(singleton);
+            }
+        }
+        return Response.status(204).build();
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityRuleRequest.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityRuleRequest.java
new file mode 100644 (file)
index 0000000..b805bd6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.northbound;
+
+import org.opendaylight.controller.networkconfig.neutron.NeutronSecurityRule;
+
+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 NeutronSecurityRuleRequest {
+    /**
+     * See OpenStack Network API v2.0 Reference for a
+     * description of annotated attributes and operations
+     */
+
+    @XmlElement(name="security_group_rule")
+    NeutronSecurityRule singletonSecurityRule;
+
+    @XmlElement(name="security_group_rules")
+    List<NeutronSecurityRule> bulkRequest;
+
+    NeutronSecurityRuleRequest() {
+    }
+
+    NeutronSecurityRuleRequest(List<NeutronSecurityRule> bulk) {
+        bulkRequest = bulk;
+        singletonSecurityRule = null;
+    }
+
+    NeutronSecurityRuleRequest(NeutronSecurityRule rule) {
+        singletonSecurityRule = rule;
+    }
+
+    public NeutronSecurityRule getSingleton() {
+        return singletonSecurityRule;
+    }
+
+    public boolean isSingleton() {
+        return (singletonSecurityRule != null);
+    }
+    public List<NeutronSecurityRule> getBulk() {
+        return bulkRequest;
+    }
+
+}
\ No newline at end of file
diff --git a/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityRulesNorthbound.java b/opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronSecurityRulesNorthbound.java
new file mode 100644 (file)
index 0000000..b2c05e0
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * 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
+ *
+ */
+
+package org.opendaylight.controller.networkconfig.neutron.northbound;
+
+
+import java.util.ArrayList;
+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.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.codehaus.enunciate.jaxrs.ResponseCode;
+import org.codehaus.enunciate.jaxrs.StatusCodes;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityGroupCRUD;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityRuleAware;
+import org.opendaylight.controller.networkconfig.neutron.INeutronSecurityRuleCRUD;
+import org.opendaylight.controller.networkconfig.neutron.NeutronCRUDInterfaces;
+import org.opendaylight.controller.networkconfig.neutron.NeutronSecurityRule;
+import org.opendaylight.controller.northbound.commons.RestMessages;
+import org.opendaylight.controller.northbound.commons.exception.BadRequestException;
+import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException;
+import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException;
+import org.opendaylight.controller.sal.utils.ServiceHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Neutron Northbound REST APIs for Security Rule.<br>
+ * This class provides REST APIs for managing neutron Security Rule
+ * <p/>
+ * <br>
+ * <br>
+ * Authentication scheme : <b>HTTP Basic</b><br>
+ * Authentication realm : <b>opendaylight</b><br>
+ * Transport : <b>HTTP and HTTPS</b><br>
+ * <br>
+ * HTTPS Authentication is disabled by default. Administrator can enable it in
+ * tomcat-server.xml after adding a proper keystore / SSL certificate from a
+ * trusted authority.<br>
+ * More info :
+ * http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration
+ */
+
+@Path ("/security-group-rules")
+public class NeutronSecurityRulesNorthbound {
+    static final Logger logger = LoggerFactory.getLogger(NeutronSecurityRulesNorthbound.class);
+
+    private NeutronSecurityRule extractFields(NeutronSecurityRule o, List<String> fields) {
+        return o.extractFields(fields);
+    }
+
+    /**
+     * Returns a list of all Security Rules
+     */
+    @GET
+    @Produces ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 200, condition = "Operation successful"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response listRules(
+            // return fields
+            @QueryParam ("fields") List<String> fields,
+            // OpenStack security rule attributes
+            @QueryParam ("id") String querySecurityRuleUUID,
+            @QueryParam ("direction") String querySecurityRuleDirection,
+            @QueryParam ("protocol") String querySecurityRuleProtocol,
+            @QueryParam ("port_range_min") Integer querySecurityRulePortMin,
+            @QueryParam ("port_range_max") Integer querySecurityRulePortMax,
+            @QueryParam ("ethertype") String querySecurityRuleEthertype,
+            @QueryParam ("remote_ip_prefix") String querySecurityRuleIpPrefix,
+            @QueryParam ("remote_group_id") String querySecurityRemoteGroupID,
+            @QueryParam ("security_group_id") String querySecurityRuleGroupID,
+            @QueryParam ("tenant_id") String querySecurityRuleTenantID,
+            @QueryParam ("limit") String limit,
+            @QueryParam ("marker") String marker,
+            @QueryParam ("page_reverse") String pageReverse
+    ) {
+        INeutronSecurityRuleCRUD securityRuleInterface = NeutronCRUDInterfaces.getINeutronSecurityRuleCRUD(this);
+        if (securityRuleInterface == null) {
+            throw new ServiceUnavailableException("Security Rule CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+        List<NeutronSecurityRule> allSecurityRules = securityRuleInterface.getAllNeutronSecurityRules();
+        List<NeutronSecurityRule> ans = new ArrayList<NeutronSecurityRule>();
+        Iterator<NeutronSecurityRule> i = allSecurityRules.iterator();
+        while (i.hasNext()) {
+            NeutronSecurityRule nsr = i.next();
+            if ((querySecurityRuleUUID == null ||
+                    querySecurityRuleUUID.equals(nsr.getSecurityRuleUUID())) &&
+                    (querySecurityRuleDirection == null ||
+                            querySecurityRuleDirection.equals(nsr.getSecurityRuleDirection())) &&
+                    (querySecurityRuleProtocol == null ||
+                            querySecurityRuleProtocol.equals(nsr.getSecurityRuleProtocol())) &&
+                    (querySecurityRulePortMin == null ||
+                            querySecurityRulePortMin.equals(nsr.getSecurityRulePortMin())) &&
+                    (querySecurityRulePortMax == null ||
+                            querySecurityRulePortMax.equals(nsr.getSecurityRulePortMax())) &&
+                    (querySecurityRuleEthertype == null ||
+                            querySecurityRuleEthertype.equals(nsr.getSecurityRuleEthertype())) &&
+                    (querySecurityRuleIpPrefix == null ||
+                            querySecurityRuleIpPrefix.equals(nsr.getSecurityRuleRemoteIpPrefix())) &&
+                    (querySecurityRuleGroupID == null ||
+                            querySecurityRuleGroupID.equals(nsr.getSecurityRuleGroupID())) &&
+                    (querySecurityRemoteGroupID == null ||
+                            querySecurityRemoteGroupID.equals(nsr.getSecurityRemoteGroupID())) &&
+                    (querySecurityRuleTenantID == null ||
+                            querySecurityRuleTenantID.equals(nsr.getSecurityRuleTenantID()))) {
+                if (fields.size() > 0) {
+                    ans.add(extractFields(nsr, fields));
+                } else {
+                    ans.add(nsr);
+                }
+            }
+        }
+        return Response.status(200).entity(
+                new NeutronSecurityRuleRequest(ans)).build();
+    }
+
+    /**
+     * Returns a specific Security Rule
+     */
+
+    @Path ("{securityRuleUUID}")
+    @GET
+    @Produces ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 200, condition = "Operation successful"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response showSecurityRule(@PathParam ("securityRuleUUID") String securityRuleUUID,
+                                     // return fields
+                                     @QueryParam ("fields") List<String> fields) {
+        INeutronSecurityRuleCRUD securityRuleInterface = NeutronCRUDInterfaces.getINeutronSecurityRuleCRUD(this);
+        if (securityRuleInterface == null) {
+            throw new ServiceUnavailableException("Security Rule CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+        if (!securityRuleInterface.neutronSecurityRuleExists(securityRuleUUID)) {
+            throw new ResourceNotFoundException("Security Rule UUID does not exist.");
+        }
+        if (!fields.isEmpty()) {
+            NeutronSecurityRule ans = securityRuleInterface.getNeutronSecurityRule(securityRuleUUID);
+            return Response.status(200).entity(
+                    new NeutronSecurityRuleRequest(extractFields(ans, fields))).build();
+        } else {
+            return Response.status(200).entity(new NeutronSecurityRuleRequest(securityRuleInterface.getNeutronSecurityRule(securityRuleUUID))).build();
+        }
+    }
+
+    /**
+     * Creates new Security Rule
+     */
+
+    @POST
+    @Produces ({MediaType.APPLICATION_JSON})
+    @Consumes ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 201, condition = "Created"),
+            @ResponseCode (code = 400, condition = "Bad Request"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 403, condition = "Forbidden"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 409, condition = "Conflict"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response createSecurityRules(final NeutronSecurityRuleRequest input) {
+        INeutronSecurityRuleCRUD securityRuleInterface = NeutronCRUDInterfaces.getINeutronSecurityRuleCRUD(this);
+        if (securityRuleInterface == null) {
+            throw new ServiceUnavailableException("Security Rule CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+        INeutronSecurityGroupCRUD securityGroupInterface = NeutronCRUDInterfaces.getINeutronSecurityGroupCRUD(this);
+        if (securityGroupInterface == null) {
+            throw new ServiceUnavailableException("Security Group CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        /*
+         * Existing entry checks
+        */
+
+        if (input.isSingleton()) {
+            NeutronSecurityRule singleton = input.getSingleton();
+
+            if (securityRuleInterface.neutronSecurityRuleExists(singleton.getSecurityRuleUUID())) {
+                throw new BadRequestException("Security Rule UUID already exists");
+            }
+            Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityRuleAware.class, this, null);
+            if (instances != null) {
+                for (Object instance : instances) {
+                    INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                    int status = service.canCreateNeutronSecurityRule(singleton);
+                    if ((status < 200) || (status > 299)) {
+                        return Response.status(status).build();
+                    }
+                }
+            }
+
+            // add rule to cache
+            singleton.initDefaults();
+            securityRuleInterface.addNeutronSecurityRule(singleton);
+            if (instances != null) {
+                for (Object instance : instances) {
+                    INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                    service.neutronSecurityRuleCreated(singleton);
+                }
+            }
+
+            securityRuleInterface.addNeutronSecurityRule(singleton);
+            if (instances != null) {
+                for (Object instance : instances) {
+                    INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                    service.neutronSecurityRuleCreated(singleton);
+                }
+            }
+        } else {
+            List<NeutronSecurityRule> bulk = input.getBulk();
+            Iterator<NeutronSecurityRule> i = bulk.iterator();
+            HashMap<String, NeutronSecurityRule> testMap = new HashMap<String, NeutronSecurityRule>();
+            Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityRuleAware.class, this, null);
+            while (i.hasNext()) {
+                NeutronSecurityRule test = i.next();
+
+                /*
+                 *  Verify that the security rule doesn't already exist
+                 */
+
+                if (securityRuleInterface.neutronSecurityRuleExists(test.getSecurityRuleUUID())) {
+                    throw new BadRequestException("Security Rule UUID already exists");
+                }
+                if (testMap.containsKey(test.getSecurityRuleUUID())) {
+                    throw new BadRequestException("Security Rule UUID already exists");
+                }
+                if (instances != null) {
+                    for (Object instance : instances) {
+                        INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                        int status = service.canCreateNeutronSecurityRule(test);
+                        if ((status < 200) || (status > 299)) {
+                            return Response.status(status).build();
+                        }
+                    }
+                }
+            }
+
+            /*
+             * now, each element of the bulk request can be added to the cache
+             */
+            i = bulk.iterator();
+            while (i.hasNext()) {
+                NeutronSecurityRule test = i.next();
+                securityRuleInterface.addNeutronSecurityRule(test);
+                if (instances != null) {
+                    for (Object instance : instances) {
+                        INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                        service.neutronSecurityRuleCreated(test);
+                    }
+                }
+            }
+        }
+        return Response.status(201).entity(input).build();
+    }
+
+    /**
+     * Updates a Security Rule
+     */
+
+    @Path ("{securityRuleUUID}")
+    @PUT
+    @Produces ({MediaType.APPLICATION_JSON})
+    @Consumes ({MediaType.APPLICATION_JSON})
+    @StatusCodes ({
+            @ResponseCode (code = 200, condition = "Operation successful"),
+            @ResponseCode (code = 400, condition = "Bad Request"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 403, condition = "Forbidden"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response updateSecurityRule(
+            @PathParam ("securityRuleUUID") String securityRuleUUID, final NeutronSecurityRuleRequest input) {
+        INeutronSecurityRuleCRUD securityRuleInterface = NeutronCRUDInterfaces.getINeutronSecurityRuleCRUD(this);
+        if (securityRuleInterface == null) {
+            throw new ServiceUnavailableException("Security Rule CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        /*
+         * verify the Security Rule exists and there is only one delta provided
+         */
+        if (!securityRuleInterface.neutronSecurityRuleExists(securityRuleUUID)) {
+            throw new ResourceNotFoundException("Security Rule UUID does not exist.");
+        }
+        if (!input.isSingleton()) {
+            throw new BadRequestException("Only singleton edit supported");
+        }
+        NeutronSecurityRule delta = input.getSingleton();
+        NeutronSecurityRule original = securityRuleInterface.getNeutronSecurityRule(securityRuleUUID);
+
+        /*
+         * updates restricted by Neutron
+         *
+         */
+        if (delta.getSecurityRuleUUID() != null ||
+                delta.getSecurityRuleDirection() != null ||
+                delta.getSecurityRuleProtocol() != null ||
+                delta.getSecurityRulePortMin() != null ||
+                delta.getSecurityRulePortMax() != null ||
+                delta.getSecurityRuleEthertype() != null ||
+                delta.getSecurityRuleRemoteIpPrefix() != null ||
+                delta.getSecurityRuleGroupID() != null ||
+                delta.getSecurityRemoteGroupID() != null ||
+                delta.getSecurityRuleTenantID() != null) {
+            throw new BadRequestException("Attribute edit blocked by Neutron");
+        }
+
+        Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityRuleAware.class, this, null);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                int status = service.canUpdateNeutronSecurityRule(delta, original);
+                if (status < 200 || status > 299) {
+                    return Response.status(status).build();
+                }
+            }
+        }
+
+        /*
+         * update the object and return it
+         */
+        securityRuleInterface.updateNeutronSecurityRule(securityRuleUUID, delta);
+        NeutronSecurityRule updatedSecurityRule = securityRuleInterface.getNeutronSecurityRule(securityRuleUUID);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                service.neutronSecurityRuleUpdated(updatedSecurityRule);
+            }
+        }
+        return Response.status(200).entity(new NeutronSecurityRuleRequest(securityRuleInterface.getNeutronSecurityRule(securityRuleUUID))).build();
+    }
+
+    /**
+     * Deletes a Security Rule
+     */
+
+    @Path ("{securityRuleUUID}")
+    @DELETE
+    @StatusCodes ({
+            @ResponseCode (code = 204, condition = "No Content"),
+            @ResponseCode (code = 401, condition = "Unauthorized"),
+            @ResponseCode (code = 404, condition = "Not Found"),
+            @ResponseCode (code = 409, condition = "Conflict"),
+            @ResponseCode (code = 501, condition = "Not Implemented")})
+    public Response deleteSecurityRule(
+            @PathParam ("securityRuleUUID") String securityRuleUUID) {
+        INeutronSecurityRuleCRUD securityRuleInterface = NeutronCRUDInterfaces.getINeutronSecurityRuleCRUD(this);
+        if (securityRuleInterface == null) {
+            throw new ServiceUnavailableException("Security Rule CRUD Interface "
+                    + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        /*
+         * verify the Security Rule exists and it isn't currently in use
+         */
+        if (!securityRuleInterface.neutronSecurityRuleExists(securityRuleUUID)) {
+            throw new ResourceNotFoundException("Security Rule UUID does not exist.");
+        }
+        if (securityRuleInterface.neutronSecurityRuleInUse(securityRuleUUID)) {
+            return Response.status(409).build();
+        }
+        NeutronSecurityRule singleton = securityRuleInterface.getNeutronSecurityRule(securityRuleUUID);
+        Object[] instances = ServiceHelper.getGlobalInstances(INeutronSecurityRuleAware.class, this, null);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                int status = service.canDeleteNeutronSecurityRule(singleton);
+                if (status < 200 || status > 299) {
+                    return Response.status(status).build();
+                }
+            }
+        }
+
+        /*
+         * remove it and return 204 status
+         */
+        securityRuleInterface.removeNeutronSecurityRule(securityRuleUUID);
+        if (instances != null) {
+            for (Object instance : instances) {
+                INeutronSecurityRuleAware service = (INeutronSecurityRuleAware) instance;
+                service.neutronSecurityRuleDeleted(singleton);
+            }
+        }
+        return Response.status(204).build();
+    }
+}
\ No newline at end of file