From: Ed Warnicke Date: Thu, 21 Aug 2014 14:38:56 +0000 (+0000) Subject: Merge "Bug 1586: Do not use JaxRS 2.0 unnecessarily" X-Git-Tag: release/helium~245 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=479196a23d56650ffbb9b6983a18cdc5d6b7ef94;hp=1d202c481a5e6cf1d8a029ae17b5e417cb4b72cd Merge "Bug 1586: Do not use JaxRS 2.0 unnecessarily" --- diff --git a/features/config-netty/pom.xml b/features/config-netty/pom.xml index 5fbc463df3..bf036979cf 100644 --- a/features/config-netty/pom.xml +++ b/features/config-netty/pom.xml @@ -45,6 +45,14 @@ org.opendaylight.controller config-netty-config + + ${config.version} + xml + config diff --git a/features/config/pom.xml b/features/config/pom.xml index 8c061c2736..20feceb360 100644 --- a/features/config/pom.xml +++ b/features/config/pom.xml @@ -16,6 +16,13 @@ + + + org.opendaylight.controller + opendaylight-karaf-empty + 1.4.2-SNAPSHOT + zip + org.opendaylight.yangtools features-yangtools diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index e7b825c48b..ac6b82b26f 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -83,6 +83,9 @@ org.opendaylight.controller md-sal-config + ${mdsal.version} + xml + config org.opendaylight.controller @@ -99,6 +102,9 @@ org.opendaylight.controller netconf-connector-config + ${netconf.version} + xml + config org.opendaylight.controller @@ -151,6 +157,9 @@ org.opendaylight.controller sal-rest-connector-config + ${mdsal.version} + xml + config org.opendaylight.controller.samples @@ -167,6 +176,9 @@ org.opendaylight.controller.samples toaster-config + ${mdsal.version} + xml + config diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java index eb5ae4a9d3..6308f2a9b0 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java @@ -8,11 +8,14 @@ package org.opendaylight.controller.frm.reconil; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; - import org.opendaylight.controller.frm.AbstractChangeListener; import org.opendaylight.controller.frm.FlowCookieProducer; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; @@ -38,10 +41,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.ListenableFuture; - /** * forwardingrules-manager * org.opendaylight.controller.frm @@ -65,7 +64,7 @@ public class FlowNodeReconcilListener extends AbstractChangeListener { } @Override - public void onDataChanged(AsyncDataChangeEvent, DataObject> changeEvent) { + public void onDataChanged(final AsyncDataChangeEvent, DataObject> changeEvent) { /* FlowCapableNode DataObjects for reconciliation */ final Set, DataObject>> createdEntries = changeEvent.getCreatedData().entrySet(); @@ -118,34 +117,46 @@ public class FlowNodeReconcilListener extends AbstractChangeListener { final InstanceIdentifier nodeIdent = identifier.firstIdentifierOf(Node.class); final NodeRef nodeRef = new NodeRef(nodeIdent); /* Groups - have to be first */ - for (Group group : flowCapNode.get().getGroup()) { - final GroupRef groupRef = new GroupRef(flowNodeIdent.child(Group.class, group.getKey())); - final AddGroupInputBuilder groupBuilder = new AddGroupInputBuilder(group); - groupBuilder.setGroupRef(groupRef); - groupBuilder.setNode(nodeRef); - this.provider.getSalGroupService().addGroup(groupBuilder.build()); + List groups = flowCapNode.get().getGroup(); + if(groups != null) { + for (Group group : groups) { + final GroupRef groupRef = new GroupRef(flowNodeIdent.child(Group.class, group.getKey())); + final AddGroupInputBuilder groupBuilder = new AddGroupInputBuilder(group); + groupBuilder.setGroupRef(groupRef); + groupBuilder.setNode(nodeRef); + this.provider.getSalGroupService().addGroup(groupBuilder.build()); + } } /* Meters */ - for (Meter meter : flowCapNode.get().getMeter()) { - final MeterRef meterRef = new MeterRef(flowNodeIdent.child(Meter.class, meter.getKey())); - final AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder(meter); - meterBuilder.setMeterRef(meterRef); - meterBuilder.setNode(nodeRef); - this.provider.getSalMeterService().addMeter(meterBuilder.build()); + List meters = flowCapNode.get().getMeter(); + if(meters != null) { + for (Meter meter : meters) { + final MeterRef meterRef = new MeterRef(flowNodeIdent.child(Meter.class, meter.getKey())); + final AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder(meter); + meterBuilder.setMeterRef(meterRef); + meterBuilder.setNode(nodeRef); + this.provider.getSalMeterService().addMeter(meterBuilder.build()); + } } /* Flows */ - for (Table flowTable : flowCapNode.get().getTable()) { - final InstanceIdentifier tableIdent = flowNodeIdent.child(Table.class, flowTable.getKey()); - for (Flow flow : flowTable.getFlow()) { - final FlowCookie flowCookie = new FlowCookie(FlowCookieProducer.INSTANCE.getNewCookie(tableIdent)); - final FlowRef flowRef = new FlowRef(tableIdent.child(Flow.class, flow.getKey())); - final FlowTableRef flowTableRef = new FlowTableRef(tableIdent); - final AddFlowInputBuilder flowBuilder = new AddFlowInputBuilder(flow); - flowBuilder.setCookie(flowCookie); - flowBuilder.setNode(nodeRef); - flowBuilder.setFlowTable(flowTableRef); - flowBuilder.setFlowRef(flowRef); - this.provider.getSalFlowService().addFlow(flowBuilder.build()); + List
tables = flowCapNode.get().getTable(); + if(tables != null) { + for (Table flowTable : tables) { + final InstanceIdentifier
tableIdent = flowNodeIdent.child(Table.class, flowTable.getKey()); + List flows = flowTable.getFlow(); + if(flows != null) { + for (Flow flow : flows) { + final FlowCookie flowCookie = new FlowCookie(FlowCookieProducer.INSTANCE.getNewCookie(tableIdent)); + final FlowRef flowRef = new FlowRef(tableIdent.child(Flow.class, flow.getKey())); + final FlowTableRef flowTableRef = new FlowTableRef(tableIdent); + final AddFlowInputBuilder flowBuilder = new AddFlowInputBuilder(flow); + flowBuilder.setCookie(flowCookie); + flowBuilder.setNode(nodeRef); + flowBuilder.setFlowTable(flowTableRef); + flowBuilder.setFlowRef(flowRef); + this.provider.getSalFlowService().addFlow(flowBuilder.build()); + } + } } } } @@ -153,7 +164,7 @@ public class FlowNodeReconcilListener extends AbstractChangeListener { @Override protected void update(final InstanceIdentifier identifier, - final DataObject original, DataObject update) { + final DataObject original, final DataObject update) { // NOOP - Listener is registered for DataChangeScope.BASE only } diff --git a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java index 29ac12393a..1e77a5554f 100644 --- a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java +++ b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java @@ -13,15 +13,19 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; +import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChain; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -class FlowCapableInventoryProvider implements AutoCloseable, Runnable { +class FlowCapableInventoryProvider implements AutoCloseable, Runnable, TransactionChainListener { private static final Logger LOG = LoggerFactory.getLogger(FlowCapableInventoryProvider.class); private static final int QUEUE_DEPTH = 500; private static final int MAX_BATCH = 100; @@ -30,6 +34,7 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { private final NotificationProviderService notificationService; private final DataBroker dataBroker; + private BindingTransactionChain txChain; private ListenerRegistration listenerRegistration; private Thread thread; @@ -42,6 +47,7 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { final NodeChangeCommiter changeCommiter = new NodeChangeCommiter(FlowCapableInventoryProvider.this); this.listenerRegistration = this.notificationService.registerNotificationListener(changeCommiter); + this.txChain = dataBroker.createTransactionChain(this); thread = new Thread(this); thread.setDaemon(true); thread.setName("FlowCapableInventoryProvider"); @@ -75,6 +81,10 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { thread.join(); thread = null; } + if(txChain != null) { + txChain.close(); + txChain = null; + } } @@ -85,7 +95,7 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { for (; ; ) { InventoryOperation op = queue.take(); - final ReadWriteTransaction tx = dataBroker.newReadWriteTransaction(); + final ReadWriteTransaction tx = txChain.newReadWriteTransaction(); LOG.debug("New operations available, starting transaction {}", tx.getIdentifier()); int ops = 0; @@ -105,12 +115,12 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { final CheckedFuture result = tx.submit(); Futures.addCallback(result, new FutureCallback() { @Override - public void onSuccess(Void aVoid) { + public void onSuccess(final Void aVoid) { //NOOP } @Override - public void onFailure(Throwable throwable) { + public void onFailure(final Throwable throwable) { LOG.error("Transaction {} failed.", tx.getIdentifier(), throwable); } }); @@ -124,4 +134,16 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { queue.poll(); } } + + @Override + public void onTransactionChainFailed(final TransactionChain chain, final AsyncTransaction transaction, + final Throwable cause) { + LOG.error("Failed to export Flow Capable Inventory, Transaction {} failed.",transaction.getIdentifier(),cause); + + } + + @Override + public void onTransactionChainSuccessful(final TransactionChain chain) { + // NOOP + } } diff --git a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java index 57ec893076..b14bfd429c 100644 --- a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java +++ b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java @@ -32,8 +32,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.No import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; @@ -81,7 +79,7 @@ class NodeChangeCommiter implements OpendaylightInventoryListener { InstanceIdentifier value = (InstanceIdentifier) ref.getValue(); LOG.debug("updating node connector : {}.", value); NodeConnector build = data.build(); - tx.put(LogicalDatastoreType.OPERATIONAL, value, build); + tx.merge(LogicalDatastoreType.OPERATIONAL, value, build, true); } }); } @@ -139,13 +137,9 @@ class NodeChangeCommiter implements OpendaylightInventoryListener { manager.enqueue(new InventoryOperation() { @Override public void applyOperation(final ReadWriteTransaction tx) { - final NodeBuilder nodeBuilder = new NodeBuilder(node); - nodeBuilder.setKey(new NodeKey(node.getId())); - final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode); - nodeBuilder.addAugmentation(FlowCapableNode.class, augment); LOG.debug("updating node :{} ", path); - tx.put(LogicalDatastoreType.OPERATIONAL, path, augment); + tx.merge(LogicalDatastoreType.OPERATIONAL, path, augment, true); } }); } diff --git a/opendaylight/md-sal/sal-binding-broker/pom.xml b/opendaylight/md-sal/sal-binding-broker/pom.xml index 74cceb1cbd..539f9d45c8 100644 --- a/opendaylight/md-sal/sal-binding-broker/pom.xml +++ b/opendaylight/md-sal/sal-binding-broker/pom.xml @@ -67,6 +67,11 @@ org.opendaylight.yangtools binding-generator-impl + + org.opendaylight.yangtools + binding-data-codec + 0.6.2-SNAPSHOT + org.opendaylight.yangtools yang-data-impl diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java index 018e26878c..93d99c832f 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java @@ -2,14 +2,13 @@ package org.opendaylight.controller.config.yang.md.sal.binding.impl; import java.util.Collection; import java.util.Collections; - +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; public class BindingAsyncDataBrokerImplModule extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModule implements @@ -36,7 +35,7 @@ public class BindingAsyncDataBrokerImplModule extends @Override public java.lang.AutoCloseable createInstance() { Broker domBroker = getDomAsyncBrokerDependency(); - BindingIndependentMappingService mappingService = getBindingMappingServiceDependency(); + BindingToNormalizedNodeCodec mappingService = getBindingMappingServiceDependency(); // FIXME: Switch this to DOM Broker registration which would not require // BundleContext when API are updated. diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java deleted file mode 100644 index 4a4e800078..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java +++ /dev/null @@ -1,79 +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.config.yang.md.sal.binding.impl; - -import java.util.concurrent.ExecutorService; - -import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; -import org.opendaylight.controller.sal.binding.impl.RootDataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingDomConnectorDeployer; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; -import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedDataBrokerImpl; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; - -/** -* -*/ -public final class DataBrokerImplModule extends - org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModule { - - public DataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, - final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public DataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, - final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - final DataBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void validate() { - super.validate(); - } - - @Override - public java.lang.AutoCloseable createInstance() { - RootDataBrokerImpl dataBindingBroker; - - - ExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor(); - BindingIndependentMappingService potentialMapping = getMappingServiceDependency(); - if (getDomBrokerDependency() != null && potentialMapping != null) { - - dataBindingBroker = createDomConnectedBroker(listeningExecutor,potentialMapping); - } else { - dataBindingBroker = createStandAloneBroker(listeningExecutor); - } - dataBindingBroker.registerRuntimeBean(getRootRuntimeBeanRegistratorWrapper()); - dataBindingBroker.setNotificationExecutor(SingletonHolder.getDefaultChangeEventExecutor()); - return dataBindingBroker; - } - - - private RootDataBrokerImpl createStandAloneBroker(final ExecutorService listeningExecutor) { - RootDataBrokerImpl broker = new RootDataBrokerImpl(); - broker.setExecutor(listeningExecutor); - return broker; - } - - private RootDataBrokerImpl createDomConnectedBroker(final ExecutorService listeningExecutor, final BindingIndependentMappingService mappingService) { - DomForwardedDataBrokerImpl forwardedBroker = new DomForwardedDataBrokerImpl(); - forwardedBroker.setExecutor(listeningExecutor); - BindingIndependentConnector connector = BindingDomConnectorDeployer.createConnector(mappingService); - getDomBrokerDependency().registerProvider(forwardedBroker, null); - ProviderSession domContext = forwardedBroker.getDomProviderContext(); - forwardedBroker.setConnector(connector); - forwardedBroker.setDomProviderContext(domContext); - forwardedBroker.startForwarding(); - return forwardedBroker; - } - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModuleFactory.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModuleFactory.java deleted file mode 100644 index d3fc5ac215..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModuleFactory.java +++ /dev/null @@ -1,17 +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.config.yang.md.sal.binding.impl; - - -/** -* -*/ -public class DataBrokerImplModuleFactory extends - org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModuleFactory { - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java index 0ea30f7e66..2bc673adff 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java @@ -7,9 +7,10 @@ */ package org.opendaylight.controller.config.yang.md.sal.binding.impl; +import com.google.common.util.concurrent.ListeningExecutorService; import java.util.Collection; import java.util.Collections; - +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; @@ -18,9 +19,6 @@ import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; - -import com.google.common.util.concurrent.ListeningExecutorService; /** * @@ -51,7 +49,7 @@ public final class ForwardedCompatibleDataBrokerImplModule extends @Override public java.lang.AutoCloseable createInstance() { ListeningExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor(); - BindingIndependentMappingService mappingService = getBindingMappingServiceDependency(); + BindingToNormalizedNodeCodec mappingService = getBindingMappingServiceDependency(); Broker domBroker = getDomAsyncBrokerDependency(); ProviderSession session = domBroker.registerProvider(this, null); @@ -60,7 +58,7 @@ public final class ForwardedCompatibleDataBrokerImplModule extends ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker, mappingService, schemaService,listeningExecutor); - dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency())); + dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(mappingService.getLegacy())); dataBroker.setDomProviderContext(session); return dataBroker; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java index b0c2d742e2..a15b1d746c 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java @@ -7,12 +7,18 @@ */ package org.opendaylight.controller.config.yang.md.sal.binding.impl; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; import java.util.Hashtable; import java.util.Map.Entry; import java.util.Set; - +import javassist.ClassPool; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; import org.opendaylight.yangtools.concepts.Delegator; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -29,9 +35,6 @@ import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; - /** * */ @@ -42,14 +45,14 @@ public final class RuntimeMappingModule extends private BundleContext bundleContext; - public RuntimeMappingModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + public RuntimeMappingModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } - public RuntimeMappingModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - RuntimeMappingModule oldModule, java.lang.AutoCloseable oldInstance) { + public RuntimeMappingModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + final RuntimeMappingModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @@ -61,41 +64,48 @@ public final class RuntimeMappingModule extends } @Override - public boolean canReuseInstance(AbstractRuntimeMappingModule oldModule) { + public boolean canReuseInstance(final AbstractRuntimeMappingModule oldModule) { return true; } @Override public java.lang.AutoCloseable createInstance() { + final GeneratedClassLoadingStrategy classLoading = getGlobalClassLoadingStrategy(); + final BindingIndependentMappingService legacyMapping = getGlobalLegacyMappingService(classLoading); + BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(new StreamWriterGenerator(SingletonHolder.JAVASSIST)); + BindingToNormalizedNodeCodec instance = new BindingToNormalizedNodeCodec(classLoading, legacyMapping, codecRegistry); + bundleContext.registerService(SchemaContextListener.class, instance, new Hashtable()); + return instance; + } - RuntimeGeneratedMappingServiceProxy potential = tryToReuseGlobalInstance(); - if(potential != null) { - return potential; + private BindingIndependentMappingService getGlobalLegacyMappingService(final GeneratedClassLoadingStrategy classLoading) { + BindingIndependentMappingService potential = tryToReuseGlobalMappingServiceInstance(); + if(potential == null) { + potential = new RuntimeGeneratedMappingServiceImpl(ClassPool.getDefault(),classLoading); + bundleContext.registerService(SchemaContextListener.class, (SchemaContextListener) potential, new Hashtable()); } + return potential; + } - final RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl(SingletonHolder.CLASS_POOL); - bundleContext.registerService(SchemaContextListener.class, service, new Hashtable()); - return service; + private GeneratedClassLoadingStrategy getGlobalClassLoadingStrategy() { + ServiceReference ref = bundleContext.getServiceReference(GeneratedClassLoadingStrategy.class); + return bundleContext.getService(ref); } - private RuntimeGeneratedMappingServiceProxy tryToReuseGlobalInstance() { + private BindingIndependentMappingService tryToReuseGlobalMappingServiceInstance() { ServiceReference serviceRef = getBundleContext().getServiceReference(BindingIndependentMappingService.class); if(serviceRef == null) { return null; } + return bundleContext.getService(serviceRef); - BindingIndependentMappingService delegate = bundleContext.getService(serviceRef); - if (delegate == null) { - return null; - } - return new RuntimeGeneratedMappingServiceProxy(getBundleContext(),serviceRef,delegate); } private BundleContext getBundleContext() { return bundleContext; } - public void setBundleContext(BundleContext bundleContext) { + public void setBundleContext(final BundleContext bundleContext) { this.bundleContext = bundleContext; } @@ -108,9 +118,9 @@ public final class RuntimeMappingModule extends private ServiceReference reference; private BundleContext bundleContext; - public RuntimeGeneratedMappingServiceProxy(BundleContext bundleContext, - ServiceReference serviceRef, - BindingIndependentMappingService delegate) { + public RuntimeGeneratedMappingServiceProxy(final BundleContext bundleContext, + final ServiceReference serviceRef, + final BindingIndependentMappingService delegate) { this.bundleContext = Preconditions.checkNotNull(bundleContext); this.reference = Preconditions.checkNotNull(serviceRef); this.delegate = Preconditions.checkNotNull(delegate); @@ -122,47 +132,47 @@ public final class RuntimeMappingModule extends } @Override - public CompositeNode toDataDom(DataObject data) { + public CompositeNode toDataDom(final DataObject data) { return delegate.toDataDom(data); } @Override public Entry toDataDom( - Entry, DataObject> entry) { + final Entry, DataObject> entry) { return delegate.toDataDom(entry); } @Override public YangInstanceIdentifier toDataDom( - org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { + final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { return delegate.toDataDom(path); } @Override public DataObject dataObjectFromDataDom( - org.opendaylight.yangtools.yang.binding.InstanceIdentifier path, - CompositeNode result) throws DeserializationException { + final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path, + final CompositeNode result) throws DeserializationException { return delegate.dataObjectFromDataDom(path, result); } @Override - public org.opendaylight.yangtools.yang.binding.InstanceIdentifier fromDataDom(YangInstanceIdentifier entry) + public org.opendaylight.yangtools.yang.binding.InstanceIdentifier fromDataDom(final YangInstanceIdentifier entry) throws DeserializationException { return delegate.fromDataDom(entry); } @Override - public Set getRpcQNamesFor(Class service) { + public Set getRpcQNamesFor(final Class service) { return delegate.getRpcQNamesFor(service); } @Override - public Optional> getRpcServiceClassFor(String namespace, String revision) { + public Optional> getRpcServiceClassFor(final String namespace, final String revision) { return delegate.getRpcServiceClassFor(namespace,revision); } @Override - public DataContainer dataObjectFromDataDom(Class inputClass, CompositeNode domInput) { + public DataContainer dataObjectFromDataDom(final Class inputClass, final CompositeNode domInput) { return delegate.dataObjectFromDataDom(inputClass, domInput); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java index e632e6336a..f843b23f9b 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java @@ -9,7 +9,6 @@ package org.opendaylight.controller.md.sal.binding.impl; import com.google.common.base.Objects; import com.google.common.base.Optional; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -19,7 +18,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; @@ -37,33 +35,28 @@ import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class AbstractForwardedDataBroker implements Delegator, DomForwardedBroker, SchemaContextListener, AutoCloseable { +public abstract class AbstractForwardedDataBroker implements Delegator, DomForwardedBroker, + SchemaContextListener, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class); // The Broker to whom we do all forwarding private final DOMDataBroker domDataBroker; - // Mapper to convert from Binding Independent objects to Binding Aware - // objects - private final BindingIndependentMappingService mappingService; - private final BindingToNormalizedNodeCodec codec; private BindingIndependentConnector connector; private ProviderSession context; private final ListenerRegistration schemaListenerRegistration; - protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, - final BindingIndependentMappingService mappingService,final SchemaService schemaService) { + protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec, + final SchemaService schemaService) { this.domDataBroker = domDataBroker; - this.mappingService = mappingService; - this.codec = new BindingToNormalizedNodeCodec(mappingService); + this.codec = codec; this.schemaListenerRegistration = schemaService.registerSchemaContextListener(this); } @@ -71,10 +64,6 @@ public abstract class AbstractForwardedDataBroker implements Delegator registerDataChangeListener(final LogicalDatastoreType store, - final InstanceIdentifier path, final DataChangeListener listener, - final DataChangeScope triggeringScope) { + final InstanceIdentifier path, final DataChangeListener listener, final DataChangeScope triggeringScope) { DOMDataChangeListener domDataChangeListener = new TranslatingDataChangeInvoker(store, path, listener, triggeringScope); YangInstanceIdentifier domPath = codec.toNormalized(path); @@ -96,23 +84,16 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> toBinding( - InstanceIdentifier path, + protected Map, DataObject> toBinding(final InstanceIdentifier path, final Map> normalized) { Map, DataObject> newMap = new HashMap<>(); for (Map.Entry> entry : sortedEntries(normalized)) { try { - Optional, DataObject>> potential = getCodec().toBinding( - entry); + Optional, DataObject>> potential = getCodec().toBinding(entry); if (potential.isPresent()) { Entry, DataObject> binding = potential.get(); newMap.put(binding.getKey(), binding.getValue()); - } else if (entry.getKey().getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) { - DataObject bindingDataObject = getCodec().toBinding(path, entry.getValue()); - if (bindingDataObject != null) { - newMap.put(path, bindingDataObject); - } } } catch (DeserializationException e) { LOG.warn("Failed to transform {}, omitting it", entry, e); @@ -123,8 +104,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> MAP_ENTRY_COMPARATOR = new Comparator>() { @Override - public int compare(final Entry left, - final Entry right) { + public int compare(final Entry left, final Entry right) { final Iterator li = left.getKey().getPathArguments().iterator(); final Iterator ri = right.getKey().getPathArguments().iterator(); @@ -144,7 +124,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator Iterable> sortedEntries(final Map map) { + private static Iterable> sortedEntries(final Map map) { if (!map.isEmpty()) { ArrayList> entries = new ArrayList<>(map.entrySet()); Collections.sort(entries, MAP_ENTRY_COMPARATOR); @@ -154,7 +134,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> toBinding(InstanceIdentifier path, + protected Set> toBinding(final InstanceIdentifier path, final Set normalized) { Set> hashSet = new HashSet<>(); for (YangInstanceIdentifier normalizedPath : normalized) { @@ -177,12 +157,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> of(data)); } private class TranslatingDataChangeInvoker implements DOMDataChangeListener { @@ -200,8 +175,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> change) { + public void onDataChanged(final AsyncDataChangeEvent> change) { bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change, path)); } } @@ -261,7 +235,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator binding) { - - // Used instance-identifier codec do not support serialization of last - // path - // argument if it is Augmentation (behaviour expected by old datastore) - // in this case, we explicitly check if last argument is augmentation - // to process it separately - if (isAugmentationIdentifier(binding)) { - return toNormalizedAugmented(binding); - } - return toNormalizedImpl(binding); + return codecRegistry.toYangInstanceIdentifier(binding); } + @SuppressWarnings({ "unchecked", "rawtypes" }) public Entry> toNormalizedNode( final InstanceIdentifier bindingPath, final DataObject bindingObject) { - return toNormalizedNode(toBindingEntry(bindingPath, bindingObject)); + return codecRegistry.toNormalizedNode((InstanceIdentifier) bindingPath, bindingObject); } public Entry> toNormalizedNode( final Entry, DataObject> binding) { - Entry legacyEntry = bindingToLegacy - .toDataDom(binding); - Entry> normalizedEntry = legacyToNormalized - .toNormalized(legacyEntry); - LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding, - legacyEntry, normalizedEntry); - if (isAugmentation(binding.getKey().getTargetType())) { - - for (DataContainerChild child : ((DataContainerNode) normalizedEntry - .getValue()).getValue()) { - if (child instanceof AugmentationNode) { - ImmutableList childArgs = ImmutableList. builder() - .addAll(normalizedEntry.getKey().getPathArguments()).add(child.getIdentifier()).build(); - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier - .create(childArgs); - return toDOMEntry(childPath, child); - } - } - - } - return normalizedEntry; - + return toNormalizedNode(binding.getKey(),binding.getValue()); } /** @@ -125,109 +75,13 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { public Optional> toBinding( final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) throws DeserializationException { - - PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments()); - // Used instance-identifier codec do not support serialization of last - // path - // argument if it is AugmentationIdentifier (behaviour expected by old - // datastore) - // in this case, we explicitly check if last argument is augmentation - // to process it separately - if (lastArgument instanceof AugmentationIdentifier) { - return toBindingAugmented(normalized); - } - return toBindingImpl(normalized); - } - - private Optional> toBindingAugmented( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DeserializationException { - Optional> potential = toBindingImpl(normalized); - // Shorthand check, if codec already supports deserialization - // of AugmentationIdentifier we will return - if (potential.isPresent() && isAugmentationIdentifier(potential.get())) { - return potential; - } - - int normalizedCount = getAugmentationCount(normalized); - AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments()); - - // Here we employ small trick - Binding-aware Codec injects an pointer - // to augmentation class - // if child is referenced - so we will reference child and then shorten - // path. - LOG.trace("Looking for candidates to match {}", normalized); - for (QName child : lastArgument.getPossibleChildNames()) { - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = normalized.node(child); - try { - if (isNotRepresentable(childPath)) { - LOG.trace("Path {} is not BI-representable, skipping it", childPath); - continue; - } - } catch (DataNormalizationException e) { - LOG.warn("Failed to denormalize path {}, skipping it", childPath, e); - continue; - } - - Optional> baId = toBindingImpl(childPath); - if (!baId.isPresent()) { - LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath); - continue; - } - - InstanceIdentifier potentialPath = shortenToLastAugment(baId.get()); - int potentialAugmentCount = getAugmentationCount(potentialPath); - if (potentialAugmentCount == normalizedCount) { - LOG.trace("Found matching path {}", potentialPath); - return Optional.> of(potentialPath); - } - - LOG.trace("Skipping mis-matched potential path {}", potentialPath); - } - - LOG.trace("Failed to find augmentation matching {}", normalized); - return Optional.absent(); - } - - private Optional> toBindingImpl( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DeserializationException { - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath; - try { - if (isNotRepresentable(normalized)) { - return Optional.absent(); - } - legacyPath = legacyToNormalized.toLegacy(normalized); - } catch (DataNormalizationException e) { - throw new IllegalStateException("Could not denormalize path.", e); - } - LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}", - legacyPath, normalized); - return Optional.> of(bindingToLegacy.fromDataDom(legacyPath)); - } - - private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DataNormalizationException { - DataNormalizationOperation op = findNormalizationOperation(normalized); - if (op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) { - return true; - } - if (op.isLeaf()) { - return true; + return Optional.>of(codecRegistry.fromYangInstanceIdentifier(normalized)); + } catch (IllegalArgumentException e) { + return Optional.absent(); } - return false; } - private DataNormalizationOperation findNormalizationOperation( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DataNormalizationException { - DataNormalizationOperation current = legacyToNormalized.getRootOperation(); - for (PathArgument arg : normalized.getPathArguments()) { - current = current.getChild(arg); - } - return current; - } private static final Entry, DataObject> toBindingEntry( final org.opendaylight.yangtools.yang.binding.InstanceIdentifier key, @@ -236,45 +90,19 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { key, value); } - private static final Entry> toDOMEntry( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier key, final NormalizedNode value) { - return new SimpleEntry>(key, - value); - } - - public DataObject toBinding(final InstanceIdentifier path, final NormalizedNode normalizedNode) - throws DeserializationException { - CompositeNode legacy = null; - if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) { - QName augIdentifier = BindingReflections.findQName(path.getTargetType()); - ContainerNode virtualNode = Builders.containerBuilder() // - .withNodeIdentifier(new NodeIdentifier(augIdentifier)) // - .withChild((DataContainerChild) normalizedNode) // - .build(); - legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode); - } else { - legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode); - } - - return bindingToLegacy.dataObjectFromDataDom(path, legacy); - } - public DataNormalizer getDataNormalizer() { return legacyToNormalized; } + @SuppressWarnings("unchecked") public Optional, DataObject>> toBinding( final Entry> normalized) throws DeserializationException { - Optional> potentialPath = toBinding(normalized.getKey()); - if (potentialPath.isPresent()) { - InstanceIdentifier bindingPath = potentialPath.get(); - DataObject bindingData = toBinding(bindingPath, normalized.getValue()); - if (bindingData == null) { - LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath); - } - return Optional.of(toBindingEntry(bindingPath, bindingData)); - } else { + try { + @SuppressWarnings("rawtypes") + Entry binding = codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()); + return Optional., DataObject>>fromNullable(binding); + } catch (IllegalArgumentException e) { return Optional.absent(); } } @@ -282,269 +110,11 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { @Override public void onGlobalContextUpdated(final SchemaContext arg0) { legacyToNormalized = new DataNormalizer(arg0); + codecRegistry.onBindingRuntimeContextUpdated(BindingRuntimeContext.create(classLoadingStrategy, arg0)); } - private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedAugmented( - final InstanceIdentifier augPath) { - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed = toNormalizedImpl(augPath); - // If used instance identifier codec added supports for deserialization - // of last AugmentationIdentifier we will just reuse it - if (isAugmentationIdentifier(processed)) { - return processed; - } - Optional additionalSerialized; - additionalSerialized = toNormalizedAugmentedUsingChildContainers(augPath, processed); - - if (additionalSerialized.isPresent()) { - return additionalSerialized.get(); - } - additionalSerialized = toNormalizedAugmentedUsingChildLeafs(augPath, processed); - if (additionalSerialized.isPresent()) { - return additionalSerialized.get(); - } - throw new IllegalStateException("Unabled to construct augmentation identfier for " + augPath); - } - - /** - * Tries to find correct augmentation identifier using children leafs - * - * This method uses normalized Instance Identifier of parent node to fetch - * schema and {@link BindingReflections#getModuleInfo(Class)} to learn about - * augmentation namespace, specificly, in which module it was defined. - * - * Then it uses it to filter all available augmentations for parent by - * module. After that it walks augmentations in particular module and - * pick-up first which at least one leaf name matches supplied augmentation. - * We could do this safely since YANG explicitly states that no any existing - * augmentations must differ in leaf fully qualified names. - * - * - * @param augPath - * Binding Aware Path which ends with augment - * @param parentPath - * Processed path - * @return - */ - private Optional toNormalizedAugmentedUsingChildLeafs( - final InstanceIdentifier augPath, - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) { - try { - DataNormalizationOperation parentOp = legacyToNormalized.getOperation(parentPath); - if(!parentOp.getDataSchemaNode().isPresent()) { - return Optional.absent(); - } - DataSchemaNode parentSchema = parentOp.getDataSchemaNode().get(); - if (parentSchema instanceof AugmentationTarget) { - Set augmentations = ((AugmentationTarget) parentSchema).getAvailableAugmentations(); - LOG.info("Augmentations for {}, {}", augPath, augmentations); - Optional schema = findAugmentation(augPath.getTargetType(), augmentations); - if (schema.isPresent()) { - AugmentationIdentifier augmentationIdentifier = DataNormalizationOperation - .augmentationIdentifierFrom(schema.get()); - return Optional.of(parentPath.node(augmentationIdentifier)); - } - } - } catch (DataNormalizationException e) { - throw new IllegalArgumentException(e); - } - return Optional.absent(); - } - - /** - * Creates instance identifier for augmentation child, tries to serialize it - * Instance Identifier is then shortened to last augmentation. - * - * This is for situations, where underlying codec is implementing hydrogen - * style DOM APIs (which did not supported {@link AugmentationIdentifier}.) - * - * @param augPath - * @param parentPath - * Path to parent node - * @return - */ - @SuppressWarnings("rawtypes") - private Optional toNormalizedAugmentedUsingChildContainers( - final InstanceIdentifier augPath, - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) { - for (Class augChild : BindingReflections.getChildrenClasses(augPath.getTargetType())) { - @SuppressWarnings("unchecked") - InstanceIdentifier childPath = augPath.child(augChild); - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = toNormalizedImpl(childPath); - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potentialDiscovered = shortenToLastAugmentation( - normalized, parentPath); - if (potentialDiscovered != null) { - return Optional.of(potentialDiscovered); - } - } - return Optional.absent(); - } - - private Optional findAugmentation(final Class targetType, - final Set augmentations) { - YangModuleInfo moduleInfo; - try { - moduleInfo = BindingReflections.getModuleInfo(targetType); - } catch (Exception e) { - throw new IllegalStateException(e); - } - Iterable filtered = filteredByModuleInfo(augmentations, - BindingReflections.getModuleQName(moduleInfo).getModule()); - filtered.toString(); - Set targetTypeGetters = getYangModeledGetters(targetType); - for (AugmentationSchema schema : filtered) { - for (DataSchemaNode child : schema.getChildNodes()) { - String getterName = "get" + BindingMapping.getClassName(child.getQName()); - if (targetTypeGetters.contains(getterName)) { - return Optional.of(schema); - } - } - } - return Optional.absent(); - } - - private static Iterable filteredByModuleInfo(final Iterable augmentations, - final QNameModule module) { - return Iterables.filter(augmentations, new Predicate() { - @Override - public boolean apply(final AugmentationSchema schema) { - final Collection childNodes = schema.getChildNodes(); - return !childNodes.isEmpty() && module.equals(Iterables.get(childNodes, 0).getQName().getModule()); - } - }); - } - - public static final Set getYangModeledGetters(final Class targetType) { - HashSet ret = new HashSet(); - for (Method method : targetType.getMethods()) { - if (isYangModeledGetter(method)) { - ret.add(method.getName()); - } - } - return ret; - } - - /** - * - * Returns true if supplied method represent getter for YANG modeled value - * - * @param method - * Method to be tested - * @return true if method represent getter for YANG Modeled value. - */ - private static final boolean isYangModeledGetter(final Method method) { - return !method.getName().equals("getClass") && !method.getName().equals("getImplementedInterface") - && method.getName().startsWith("get") && method.getParameterTypes().length == 0; - } - - private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier shortenToLastAugmentation( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized, - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) { - int parentSize = Iterables.size(parentPath.getPathArguments()); - int position = 0; - int foundPosition = -1; - for (PathArgument arg : normalized.getPathArguments()) { - position++; - if (arg instanceof AugmentationIdentifier) { - foundPosition = position; - } - } - if (foundPosition > 0 && foundPosition > parentSize) { - Iterable shortened = Iterables.limit(normalized.getPathArguments(), foundPosition); - return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(shortened); - } - return null; - } - - private InstanceIdentifier shortenToLastAugment( - final InstanceIdentifier binding) { - int position = 0; - int foundPosition = -1; - for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) { - position++; - if (isAugmentation(arg.getType())) { - foundPosition = position; - } - } - return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition)); - } - - private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedImpl( - final InstanceIdentifier binding) { - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath = bindingToLegacy - .toDataDom(binding); - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = legacyToNormalized - .toNormalized(legacyPath); - return normalized; - } - - private static boolean isAugmentation(final Class type) { - return Augmentation.class.isAssignableFrom(type); - } - - private static boolean isAugmentationIdentifier(final InstanceIdentifier potential) { - return Augmentation.class.isAssignableFrom(potential.getTargetType()); - } - - private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed) { - return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier; - } - - private static int getAugmentationCount(final InstanceIdentifier potential) { - int count = 0; - for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) { - if (isAugmentation(arg.getType())) { - count++; - } - - } - return count; - } - - private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potential) { - int count = 0; - for (PathArgument arg : potential.getPathArguments()) { - if (arg instanceof AugmentationIdentifier) { - count++; - } - } - return count; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) public Function>, Optional> deserializeFunction(final InstanceIdentifier path) { - return new DeserializeFunction(this, path); - } - - private static class DeserializeFunction implements Function>, Optional> { - - private final BindingToNormalizedNodeCodec codec; - private final InstanceIdentifier path; - - public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier path) { - super(); - this.codec = Preconditions.checkNotNull(codec, "Codec must not be null"); - this.path = Preconditions.checkNotNull(path, "Path must not be null"); - } - - @SuppressWarnings("rawtypes") - @Nullable - @Override - public Optional apply(@Nullable final Optional> normalizedNode) { - if (normalizedNode.isPresent()) { - final DataObject dataObject; - try { - dataObject = codec.toBinding(path, normalizedNode.get()); - } catch (DeserializationException e) { - LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e); - throw new IllegalStateException("Failed to create dataobject", e); - } - - if (dataObject != null) { - return Optional.of(dataObject); - } - } - return Optional.absent(); - } + return codecRegistry.deserializeFunction(path); } /** @@ -566,4 +136,13 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { } return currentOp.createDefault(path.getLastPathArgument()); } + + public BindingIndependentMappingService getLegacy() { + return bindingToLegacy; + } + + @Override + public void close() throws Exception { + // NOOP Intentionally + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java index 237d9678f9..52e114b0ea 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java @@ -7,6 +7,12 @@ */ package org.opendaylight.controller.md.sal.binding.impl; +import com.google.common.base.Function; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -17,7 +23,6 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; - import org.opendaylight.controller.md.sal.common.api.RegistrationListener; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; @@ -44,17 +49,9 @@ import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Function; -import com.google.common.util.concurrent.AsyncFunction; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; - @SuppressWarnings("deprecation") public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDataBroker implements DataProviderService, AutoCloseable { @@ -64,7 +61,7 @@ public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDat private final ListeningExecutorService executorService; public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker, - final BindingIndependentMappingService mappingService, final SchemaService schemaService,final ListeningExecutorService executor) { + final BindingToNormalizedNodeCodec mappingService, final SchemaService schemaService,final ListeningExecutorService executor) { super(domDataBroker, mappingService,schemaService); executorService = executor; LOG.info("ForwardedBackwardsCompatibleBroker started."); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java index 6359b60684..ef66d80ed4 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java @@ -16,7 +16,6 @@ import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; /** * The DataBrokerImpl simply defers to the DOMDataBroker for all its operations. @@ -30,8 +29,8 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping */ public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements DataBroker { - public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService, final SchemaService schemaService) { - super(domDataBroker, mappingService,schemaService); + public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec, final SchemaService schemaService) { + super(domDataBroker, codec,schemaService); } @Override diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java index 1ec4aa2d30..f037e679be 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java @@ -7,6 +7,10 @@ */ package org.opendaylight.controller.sal.binding.codegen.impl; +import com.google.common.util.concurrent.ForwardingBlockingQueue; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -16,24 +20,19 @@ import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; - import javassist.ClassPool; - import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator; import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.util.concurrent.ForwardingBlockingQueue; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.ThreadFactoryBuilder; - public class SingletonHolder { private static final Logger logger = LoggerFactory.getLogger(SingletonHolder.class); public static final ClassPool CLASS_POOL = ClassPool.getDefault(); + public static final JavassistUtils JAVASSIST = JavassistUtils.forClassPool(CLASS_POOL); public static final org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator RPC_GENERATOR_IMPL = new org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator( CLASS_POOL); public static final RuntimeCodeGenerator RPC_GENERATOR = RPC_GENERATOR_IMPL; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootDataBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootDataBrokerImpl.java deleted file mode 100644 index a1cae266c1..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootDataBrokerImpl.java +++ /dev/null @@ -1,50 +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.binding.impl; - -import org.opendaylight.controller.config.yang.md.sal.binding.impl.Data; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeMXBean; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeRegistration; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeRegistrator; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.Transactions; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; - -public class RootDataBrokerImpl extends DataBrokerImpl implements DataBrokerImplRuntimeMXBean { - - private final Transactions transactions = new Transactions(); - private final Data data = new Data(); - private BindingIndependentConnector bindingIndependentConnector; - private DataBrokerImplRuntimeRegistration runtimeBeanRegistration; - - public BindingIndependentConnector getBindingIndependentConnector() { - return bindingIndependentConnector; - } - - public Transactions getTransactions() { - transactions.setCreated(getCreatedTransactionsCount().get()); - transactions.setSubmitted(getSubmittedTransactionsCount().get()); - transactions.setSuccessful(getFinishedTransactionsCount().get()); - transactions.setFailed(getFailedTransactionsCount().get()); - return transactions; - } - - @Override - public Data getData() { - data.setTransactions(getTransactions()); - return data; - } - - public void setBindingIndependentConnector(BindingIndependentConnector runtimeMapping) { - this.bindingIndependentConnector = runtimeMapping; - } - - public void registerRuntimeBean(DataBrokerImplRuntimeRegistrator rootRegistrator) { - runtimeBeanRegistration = rootRegistrator.register(this); - } - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedDataBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedDataBrokerImpl.java deleted file mode 100644 index 3d0e4deb65..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedDataBrokerImpl.java +++ /dev/null @@ -1,56 +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.binding.impl.forward; - -import java.util.Collection; -import java.util.Collections; - -import org.opendaylight.controller.sal.binding.impl.RootDataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingDomConnectorDeployer; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; -import org.opendaylight.controller.sal.core.api.Provider; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; - -public class DomForwardedDataBrokerImpl extends RootDataBrokerImpl implements Provider, DomForwardedBroker { - - private BindingIndependentConnector connector; - private ProviderSession domProviderContext; - - public void setConnector(BindingIndependentConnector connector) { - this.connector = connector; - } - - @Override - public void onSessionInitiated(ProviderSession session) { - this.setDomProviderContext(session); - } - - @Override - public Collection getProviderFunctionality() { - return Collections.emptySet(); - } - - @Override - public BindingIndependentConnector getConnector() { - return connector; - } - - @Override - public ProviderSession getDomProviderContext() { - return domProviderContext; - } - - public void setDomProviderContext(ProviderSession domProviderContext) { - this.domProviderContext = domProviderContext; - } - - @Override - public void startForwarding() { - BindingDomConnectorDeployer.startDataForwarding(getConnector(), this, getDomProviderContext()); - } -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang index cee4b1efb3..aec2723591 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang +++ b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang @@ -18,10 +18,9 @@ module opendaylight-sal-binding-broker-impl { identity binding-dom-mapping-service { base config:service-type; - config:java-class "org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService"; + config:java-class "org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec"; } - identity binding-broker-impl { base config:module-type; config:provided-service sal:binding-broker-osgi-registry; @@ -29,13 +28,6 @@ module opendaylight-sal-binding-broker-impl { config:java-name-prefix BindingBrokerImpl; } - identity binding-data-broker { - base config:module-type; - config:provided-service sal:binding-data-broker; - config:provided-service sal:binding-data-consumer-broker; - config:java-name-prefix DataBrokerImpl; - } - identity binding-data-compatible-broker { base config:module-type; config:provided-service sal:binding-data-broker; @@ -131,29 +123,6 @@ module opendaylight-sal-binding-broker-impl { } } - augment "/config:modules/config:module/config:configuration" { - case binding-data-broker { - when "/config:modules/config:module/config:type = 'binding-data-broker'"; - container dom-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:dom-broker-osgi-registry; - } - } - } - - container mapping-service { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity binding-dom-mapping-service; - } - } - } - } - } - augment "/config:modules/config:module/config:configuration" { case binding-data-compatible-broker { when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'"; @@ -178,14 +147,6 @@ module opendaylight-sal-binding-broker-impl { } } - augment "/config:modules/config:module/config:state" { - case binding-data-broker { - when "/config:modules/config:module/config:type = 'binding-data-broker'"; - container data { - uses common:data-state; - } - } - } augment "/config:modules/config:module/config:state" { case binding-rpc-broker { when "/config:modules/config:module/config:type = 'binding-rpc-broker'"; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java index fd0a169694..815fc45259 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java @@ -1,8 +1,8 @@ package org.opendaylight.controller.md.sal.binding.impl.test; import static org.junit.Assert.assertTrue; -import javassist.ClassPool; +import javassist.ClassPool; import org.junit.Test; import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.test.AbstractSchemaAwareTest; @@ -11,7 +11,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; @@ -34,7 +38,9 @@ public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest { @Override protected void setupWithSchema(final SchemaContext context) { mappingService = new RuntimeGeneratedMappingServiceImpl(ClassPool.getDefault()); - codec = new BindingToNormalizedNodeCodec(mappingService); + StreamWriterGenerator streamWriter = new StreamWriterGenerator(JavassistUtils.forClassPool(ClassPool.getDefault())); + BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(streamWriter); + codec = new BindingToNormalizedNodeCodec(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), mappingService, registry); mappingService.onGlobalContextUpdated(context); codec.onGlobalContextUpdated(context); }; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java index 60eec55ca5..106fcea0e9 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java @@ -7,9 +7,14 @@ */ package org.opendaylight.controller.md.sal.binding.test; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + import javassist.ClassPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -19,20 +24,22 @@ import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.sal.binding.test.util.MockSchemaService; import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.controller.sal.core.spi.data.DOMStore; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - public class DataBrokerTestCustomizer { private DOMDataBroker domDataBroker; private final RuntimeGeneratedMappingServiceImpl mappingService; private final MockSchemaService schemaService; private ImmutableMap datastores; + private final BindingToNormalizedNodeCodec bindingToNormalized ; public ImmutableMap createDatastores() { return ImmutableMap.builder() @@ -43,7 +50,13 @@ public class DataBrokerTestCustomizer { public DataBrokerTestCustomizer() { schemaService = new MockSchemaService(); - mappingService = new RuntimeGeneratedMappingServiceImpl(ClassPool.getDefault()); + ClassPool pool = ClassPool.getDefault(); + mappingService = new RuntimeGeneratedMappingServiceImpl(pool); + DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(pool)); + BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator); + GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + bindingToNormalized = new BindingToNormalizedNodeCodec(loading, mappingService, codecRegistry); + schemaService.registerSchemaContextListener(bindingToNormalized); } public DOMStore createConfigurationDatastore() { @@ -69,14 +82,13 @@ public class DataBrokerTestCustomizer { } public DataBroker createDataBroker() { - return new ForwardedBindingDataBroker(getDOMDataBroker(), getMappingService(), getSchemaService()); + return new ForwardedBindingDataBroker(getDOMDataBroker(), bindingToNormalized, schemaService ); } public ForwardedBackwardsCompatibleDataBroker createBackwardsCompatibleDataBroker() { - return new ForwardedBackwardsCompatibleDataBroker(getDOMDataBroker(), getMappingService(), getSchemaService(), MoreExecutors.sameThreadExecutor()); + return new ForwardedBackwardsCompatibleDataBroker(getDOMDataBroker(), bindingToNormalized, getSchemaService(), MoreExecutors.sameThreadExecutor()); } - private SchemaService getSchemaService() { return schemaService; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java index 7b67d3b10f..63e0e2290a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; - import org.junit.Test; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; @@ -76,7 +75,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem * * @throws Exception */ - @Test( timeout = 15000) + @Test() public void testAugmentSerialization() throws Exception { baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this); @@ -122,7 +121,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem testNodeRemove(); } - private > Node createTestNode(Class augmentationClass, T augmentation) { + private > Node createTestNode(final Class augmentationClass, final T augmentation) { NodeBuilder nodeBuilder = new NodeBuilder(); nodeBuilder.setId(new NodeId(NODE_ID)); nodeBuilder.setKey(NODE_KEY); @@ -130,7 +129,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem return nodeBuilder.build(); } - private DataModificationTransaction commitNodeAndVerifyTransaction(Node original) throws Exception { + private DataModificationTransaction commitNodeAndVerifyTransaction(final Node original) throws Exception { DataModificationTransaction transaction = baDataService.beginTransaction(); transaction.putOperationalData(NODE_INSTANCE_ID_BA, original); RpcResult result = transaction.commit().get(); @@ -148,7 +147,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem assertNull(node); } - private AugmentationVerifier verifyNode(Nodes nodes, Node original) { + private AugmentationVerifier verifyNode(final Nodes nodes, final Node original) { assertNotNull(nodes); assertNotNull(nodes.getNode()); assertEquals(1, nodes.getNode().size()); @@ -158,7 +157,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem return new AugmentationVerifier(readedNode); } - private void assertBindingIndependentVersion(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier nodeId) { + private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier nodeId) { CompositeNode node = biDataService.readOperationalData(nodeId); assertNotNull(node); } @@ -171,7 +170,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem return nodeMeterStatistics(10, false); } - private NodeMeterStatistics nodeMeterStatistics(int count, boolean setDuration) { + private NodeMeterStatistics nodeMeterStatistics(final int count, final boolean setDuration) { NodeMeterStatisticsBuilder nmsb = new NodeMeterStatisticsBuilder(); MeterStatisticsBuilder meterStats = new MeterStatisticsBuilder(); @@ -207,7 +206,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem } @Override - public void onDataChanged(DataChangeEvent, DataObject> change) { + public void onDataChanged(final DataChangeEvent, DataObject> change) { receivedChangeEvent = change; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java index fef5715f50..d0a326adff 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java @@ -9,12 +9,19 @@ package org.opendaylight.controller.sal.binding.test.util; import static com.google.common.base.Preconditions.checkState; +import com.google.common.annotations.Beta; +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.ImmutableClassToInstanceMap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.MutableClassToInstanceMap; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.util.Set; import java.util.concurrent.Future; - import javassist.ClassPool; - import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -43,9 +50,14 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.dom.broker.BrokerImpl; import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl; import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.opendaylight.yangtools.yang.binding.YangModuleInfo; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.common.QName; @@ -56,15 +68,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.Beta; -import com.google.common.collect.ClassToInstanceMap; -import com.google.common.collect.ImmutableClassToInstanceMap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.MutableClassToInstanceMap; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - @Beta public class BindingTestContext implements AutoCloseable { @@ -74,6 +77,7 @@ public class BindingTestContext implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(BindingTestContext.class); private RuntimeGeneratedMappingServiceImpl mappingServiceImpl; + private BindingToNormalizedNodeCodec codec; private DomForwardedBindingBrokerImpl baBrokerImpl; private DataBrokerImpl baDataImpl; @@ -129,7 +133,7 @@ public class BindingTestContext implements AutoCloseable { public void startNewDataBroker() { checkState(executor != null, "Executor needs to be set"); checkState(newDOMDataBroker != null, "DOM Data Broker must be set"); - dataBroker = new ForwardedBindingDataBroker(newDOMDataBroker, mappingServiceImpl, mockSchemaService); + dataBroker = new ForwardedBindingDataBroker(newDOMDataBroker, codec, mockSchemaService); } public void startNewDomDataBroker() { @@ -250,6 +254,12 @@ public class BindingTestContext implements AutoCloseable { checkState(classPool != null, "ClassPool needs to be present"); mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool); mockSchemaService.registerSchemaContextListener(mappingServiceImpl); + + DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(classPool)); + BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator); + GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + codec = new BindingToNormalizedNodeCodec(loading, mappingServiceImpl, codecRegistry); + mockSchemaService.registerSchemaContextListener(codec); } private void updateYangSchema(final ImmutableSet moduleInfos) { @@ -280,7 +290,7 @@ public class BindingTestContext implements AutoCloseable { } public void startNewBindingDataBroker() { - ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl,mockSchemaService, executor); + ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, codec,mockSchemaService, executor); baData = forwarded; } diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 83a69969b7..344694381a 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -112,6 +112,7 @@ public class TestHelper { mavenBundle(YANGTOOLS, "binding-generator-api").versionAsInProject(), mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(), // mavenBundle(YANGTOOLS, "binding-generator-impl").versionAsInProject(), + mavenBundle(YANGTOOLS, "binding-data-codec").versionAsInProject(), mavenBundle(YANGTOOLS + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), // // mavenBundle(CONTROLLER, "sal-core-api").versionAsInProject().update(), // diff --git a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java index 8a390b337e..33039ea231 100644 --- a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java +++ b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java @@ -11,9 +11,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import com.google.inject.Inject; import java.util.concurrent.Future; - import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext; @@ -22,7 +23,6 @@ import org.opendaylight.controller.sal.binding.api.data.DataBrokerService; import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; import org.opendaylight.controller.sal.core.api.Broker; 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.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; @@ -31,8 +31,6 @@ import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; -import com.google.inject.Inject; - public class DataServiceTest extends AbstractTest { protected DataBrokerService consumerDataService; @@ -45,12 +43,20 @@ public class DataServiceTest extends AbstractTest { public void setUp() throws Exception { } + /* + * + * Ignored this, because classes here are constructed from + * very different class loader as MD-SAL is run into, + * this is code is run from different classloader. + * + */ @Test + @Ignore public void test() throws Exception { BindingAwareConsumer consumer1 = new BindingAwareConsumer() { @Override - public void onSessionInitialized(ConsumerContext session) { + public void onSessionInitialized(final ConsumerContext session) { consumerDataService = session.getSALService(DataBrokerService.class); } }; @@ -62,12 +68,12 @@ public class DataServiceTest extends AbstractTest { DataModificationTransaction transaction = consumerDataService.beginTransaction(); assertNotNull(transaction); - NodeRef node1 = createNodeRef("0"); - DataObject node = consumerDataService.readConfigurationData(node1.getValue()); + InstanceIdentifier node1 = createNodeRef("0"); + DataObject node = consumerDataService.readConfigurationData(node1); assertNull(node); Node nodeData1 = createNode("0"); - transaction.putConfigurationData(node1.getValue(), nodeData1); + transaction.putConfigurationData(node1, nodeData1); Future> commitResult = transaction.commit(); assertNotNull(commitResult); @@ -77,7 +83,7 @@ public class DataServiceTest extends AbstractTest { assertNotNull(result.getResult()); assertEquals(TransactionStatus.COMMITED, result.getResult()); - Node readedData = (Node) consumerDataService.readConfigurationData(node1.getValue()); + Node readedData = (Node) consumerDataService.readConfigurationData(node1); assertNotNull(readedData); assertEquals(nodeData1.getKey(), readedData.getKey()); @@ -85,7 +91,7 @@ public class DataServiceTest extends AbstractTest { DataModificationTransaction transaction2 = consumerDataService.beginTransaction(); assertNotNull(transaction); - transaction2.removeConfigurationData(node1.getValue()); + transaction2.removeConfigurationData(node1); Future> commitResult2 = transaction2.commit(); assertNotNull(commitResult2); @@ -96,21 +102,19 @@ public class DataServiceTest extends AbstractTest { assertNotNull(result2.getResult()); assertEquals(TransactionStatus.COMMITED, result2.getResult()); - DataObject readedData2 = consumerDataService.readConfigurationData(node1.getValue()); + DataObject readedData2 = consumerDataService.readConfigurationData(node1); assertNull(readedData2); } - private static NodeRef createNodeRef(String string) { + private static InstanceIdentifier createNodeRef(final String string) { NodeKey key = new NodeKey(new NodeId(string)); - InstanceIdentifier path = InstanceIdentifier.builder(Nodes.class).child(Node.class, key).build(); - - return new NodeRef(path); + return InstanceIdentifier.builder(Nodes.class).child(Node.class, key).build(); } - private static Node createNode(String string) { + private static Node createNode(final String string) { NodeBuilder ret = new NodeBuilder(); NodeId id = new NodeId(string); ret.setKey(new NodeKey(id)); diff --git a/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml b/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml index 63a921d6f3..5e37f36a2c 100644 --- a/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml +++ b/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml @@ -38,124 +38,104 @@ - - prefix:schema-service-singleton - + prefix:schema-service-singleton yang-schema-service - - prefix:hash-map-data-store - - hash-map-data-store + prefix:runtime-generated-mapping + runtime-mapping-singleton - - prefix:dom-broker-impl - - dom-broker - - - dom:dom-data-store - - ref_hash-map-data-store - + prefix:binding-notification-broker + binding-notification-broker - - prefix:binding-broker-impl - + prefix:binding-broker-impl binding-broker-impl - - - binding:binding-notification-service - - ref_binding-notification-broker + + binding:binding-notification-service + binding-notification-broker - - binding:binding-data-broker - - ref_binding-data-broker + binding:binding-data-broker + binding-data-broker + - - prefix:runtime-generated-mapping - - runtime-mapping-singleton + prefix:dom-inmemory-data-broker + inmemory-data-broker + + dom:schema-service + yang-schema-service + - - prefix:binding-notification-broker - - binding-notification-broker + prefix:dom-broker-impl + inmemory-dom-broker + + dom:dom-async-data-broker + inmemory-data-broker + - - prefix:binding-data-broker - - binding-data-broker - - - dom:dom-broker-osgi-registry - - ref_dom-broker - - - - binding:binding-dom-mapping-service - - ref_runtime-mapping-singleton - + prefix:binding-data-compatible-broker + inmemory-binding-data-broker + + dom:dom-broker-osgi-registry + dom-broker + + + binding:binding-dom-mapping-service + runtime-mapping-singleton + + + + prefix:binding-forwarded-data-broker + binding-async-data-broker + + + dom:dom-broker-osgi-registry + dom-broker + + + binding:binding-dom-mapping-service + runtime-mapping-singleton + + - - dom:schema-service - + dom:schema-service - ref_yang-schema-service - - /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service'] - + yang-schema-service + /modules/module[type='schema-service-singleton'][name='yang-schema-service'] - - binding:binding-notification-service - + binding-impl:binding-dom-mapping-service - ref_binding-notification-broker - - /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker'] - + runtime-mapping-singleton + /modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton'] - - dom:dom-data-store - + binding:binding-notification-service - ref_hash-map-data-store - - /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store'] - + binding-notification-broker + /modules/module[type='binding-notification-broker'][name='binding-notification-broker'] - - - binding:binding-broker-osgi-registry - + binding:binding-broker-osgi-registry - ref_binding-broker-impl - - /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl'] - + binding-osgi-broker + /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] @@ -165,36 +145,36 @@ /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] + + + dom:dom-broker-osgi-registry + + dom-broker + /modules/module[type='dom-broker-impl'][name='inmemory-dom-broker'] + + + - - binding-impl:binding-dom-mapping-service - + binding:binding-data-broker - ref_runtime-mapping-singleton - - /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton'] - + binding-data-broker + /modules/module[type='binding-data-compatible-broker'][name='inmemory-binding-data-broker'] + - - dom:dom-broker-osgi-registry - + binding:binding-async-data-broker - ref_dom-broker - /config/modules/module[name='dom-broker-impl']/instance[name='dom-broker'] - + binding-data-broker + /modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker'] + - - binding:binding-data-broker - + dom:dom-async-data-broker - ref_binding-data-broker - - /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] - + inmemory-data-broker + /modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker'] diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java index bfdb0930b1..1e60803f05 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java @@ -44,7 +44,7 @@ public class MeteredBoundedMailboxTest { actorSystem.eventStream().subscribe(mockReceiver.getRef(), DeadLetter.class); - final FiniteDuration ONE_SEC = new FiniteDuration(1, TimeUnit.SECONDS); + final FiniteDuration TEN_SEC = new FiniteDuration(10, TimeUnit.SECONDS); String boundedMailBox = actorSystem.name() + ".bounded-mailbox"; ActorRef pingPongActor = actorSystem.actorOf(PingPongActor.props(lock).withMailbox(boundedMailBox), "pingpongactor"); @@ -59,11 +59,11 @@ public class MeteredBoundedMailboxTest { pingPongActor.tell("ping", mockReceiver.getRef()); } - mockReceiver.expectMsgClass(ONE_SEC, DeadLetter.class); + mockReceiver.expectMsgClass(TEN_SEC, DeadLetter.class); lock.unlock(); - Object[] eleven = mockReceiver.receiveN(11, ONE_SEC); + Object[] eleven = mockReceiver.receiveN(11, TEN_SEC); } /** diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java index 3396eb5564..2972772a48 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java @@ -295,7 +295,12 @@ public class ShardManager extends AbstractUntypedActor { new Function() { @Override public SupervisorStrategy.Directive apply(Throwable t) { - LOG.warning("Supervisor Strategy of resume applied {}",t); + StringBuilder sb = new StringBuilder(); + for(StackTraceElement element : t.getStackTrace()) { + sb.append("\n\tat ") + .append(element.toString()); + } + LOG.warning("Supervisor Strategy of resume applied {}",sb.toString()); return SupervisorStrategy.resume(); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java index 25705bff41..34d3531283 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java @@ -90,7 +90,7 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { public void onFailure(Throwable t) { LOG.error(t, "An exception happened during abort"); sender - .tell(new akka.actor.Status.Failure(t), getSelf()); + .tell(new akka.actor.Status.Failure(t), self); } }); } @@ -119,7 +119,7 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { public void onFailure(Throwable t) { LOG.error(t, "An exception happened during pre-commit"); sender - .tell(new akka.actor.Status.Failure(t), getSelf()); + .tell(new akka.actor.Status.Failure(t), self); } }); @@ -138,7 +138,7 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { public void onFailure(Throwable t) { LOG.error(t, "An exception happened during canCommit"); sender - .tell(new akka.actor.Status.Failure(t), getSelf()); + .tell(new akka.actor.Status.Failure(t), self); } }); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java index 870889b350..34697977a5 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java @@ -63,7 +63,7 @@ public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { store.onGlobalContextUpdated(testSchemaContext); } - private FiniteDuration ASK_RESULT_DURATION = Duration.create(3000, TimeUnit.MILLISECONDS); + private FiniteDuration ASK_RESULT_DURATION = Duration.create(5000, TimeUnit.MILLISECONDS); @Test(expected = TestException.class) diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java index ac1f2e32d5..536cfa0081 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java @@ -7,6 +7,8 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.base.Preconditions; + import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.yangtools.util.concurrent.NotificationManager; @@ -16,35 +18,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; class ChangeListenerNotifyTask implements Runnable { - private static final Logger LOG = LoggerFactory.getLogger(ChangeListenerNotifyTask.class); - private final Iterable> listeners; - private final AsyncDataChangeEvent> event; - @SuppressWarnings("rawtypes") - private final NotificationManager - notificationMgr; + private final NotificationManager notificationMgr; + private final AsyncDataChangeEvent> event; + private final DataChangeListenerRegistration listener; @SuppressWarnings("rawtypes") - public ChangeListenerNotifyTask(final Iterable> listeners, + public ChangeListenerNotifyTask(final DataChangeListenerRegistration listener, final AsyncDataChangeEvent> event, final NotificationManager notificationMgr) { - this.listeners = listeners; - this.event = event; - this.notificationMgr = notificationMgr; + this.notificationMgr = Preconditions.checkNotNull(notificationMgr); + this.listener = Preconditions.checkNotNull(listener); + this.event = Preconditions.checkNotNull(event); } @Override public void run() { - - for (DataChangeListenerRegistration listener : listeners) { - notificationMgr.submitNotification(listener.getInstance(), event); + final AsyncDataChangeListener> l = listener.getInstance(); + if (l == null) { + LOG.trace("Skipping event delivery to unregistered listener {}", l); + return; } + LOG.trace("Listener {} event {}", l, event); + + // FIXME: Yo dawg I heard you like queues, so this was queued to be queued + notificationMgr.submitNotification(l, event); } @Override public String toString() { - return "ChangeListenerNotifyTask [listeners=" + listeners + ", event=" + event + "]"; + return "ChangeListenerNotifyTask [listener=" + listener + ", event=" + event + "]"; } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java index 5faebcef36..f457e3b9e9 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java @@ -7,6 +7,8 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.base.Preconditions; + import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -19,8 +21,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import com.google.common.base.Preconditions; - public final class DOMImmutableDataChangeEvent implements AsyncDataChangeEvent> { @@ -184,6 +184,10 @@ public final class DOMImmutableDataChangeEvent implements updated.put(path, after); return this; } + + public boolean isEmpty() { + return created.isEmpty() && removed.isEmpty() && updated.isEmpty(); + } } private static final class RemoveEventFactory implements SimpleEventFactory { diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java index d0d3fe9e6a..129013378e 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java @@ -49,7 +49,6 @@ import org.slf4j.LoggerFactory; import javax.annotation.concurrent.GuardedBy; -import java.util.Collections; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -176,7 +175,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch .addCreated(path, data) // .build(); - new ChangeListenerNotifyTask(Collections.singletonList(reg), event, + new ChangeListenerNotifyTask(reg, event, dataChangeListenerNotificationManager).run(); } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java index d8feaa71f6..a4e8c86aa8 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java @@ -7,36 +7,24 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; -import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder; - import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.Callable; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree; -import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Walker; import org.opendaylight.yangtools.util.concurrent.NotificationManager; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; @@ -54,14 +42,13 @@ import org.slf4j.LoggerFactory; */ final class ResolveDataChangeEventsTask implements Callable> { private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class); - private static final DOMImmutableDataChangeEvent NO_CHANGE = builder(DataChangeScope.BASE).build(); - private final Multimap events = HashMultimap.create(); + @SuppressWarnings("rawtypes") + private final NotificationManager notificationMgr; private final DataTreeCandidate candidate; private final ListenerTree listenerRoot; - @SuppressWarnings("rawtypes") - private final NotificationManager notificationMgr; + private Multimap, DOMImmutableDataChangeEvent> collectedEvents; @SuppressWarnings("rawtypes") public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree, @@ -81,153 +68,42 @@ final class ResolveDataChangeEventsTask implements Callable call() { + public synchronized Iterable call() { try (final Walker w = listenerRoot.getWalker()) { - resolveAnyChangeEvent(candidate.getRootPath(), Collections.singleton(w.getRootNode()), candidate.getRootNode()); - return createNotificationTasks(); - } - } - - /** - * - * Walks map of listeners to data change events, creates notification - * delivery tasks. - * - * Walks map of registered and affected listeners and creates notification - * tasks from set of listeners and events to be delivered. - * - * If set of listeners has more then one event (applicable to wildcarded - * listeners), merges all data change events into one, final which contains - * all separate updates. - * - * Dispatch between merge variant and reuse variant of notification task is - * done in - * {@link #addNotificationTask(com.google.common.collect.ImmutableList.Builder, Node, java.util.Collection)} - * - * @return Collection of notification tasks. - */ - private Collection createNotificationTasks() { - ImmutableList.Builder taskListBuilder = ImmutableList.builder(); - for (Entry> entry : events.asMap().entrySet()) { - addNotificationTask(taskListBuilder, entry.getKey(), entry.getValue()); - } - return taskListBuilder.build(); - } - - /** - * Adds notification task to task list. - * - * If entry collection contains one event, this event is reused and added to - * notification tasks for listeners (see - * {@link #addNotificationTaskByScope(com.google.common.collect.ImmutableList.Builder, Node, DOMImmutableDataChangeEvent)} - * . Otherwise events are merged by scope and distributed between listeners - * to particular scope. See - * {@link #addNotificationTasksAndMergeEvents(com.google.common.collect.ImmutableList.Builder, Node, java.util.Collection)} - * . - * - * @param taskListBuilder - * @param listeners - * @param entries - */ - private void addNotificationTask(final ImmutableList.Builder taskListBuilder, - final ListenerTree.Node listeners, final Collection entries) { - - if (!entries.isEmpty()) { - if (entries.size() == 1) { - addNotificationTaskByScope(taskListBuilder, listeners, Iterables.getOnlyElement(entries)); - } else { - addNotificationTasksAndMergeEvents(taskListBuilder, listeners, entries); - } - } - } + // Defensive: reset internal state + collectedEvents = ArrayListMultimap.create(); - /** - * - * Add notification deliveries task to the listener. - * - * - * @param taskListBuilder - * @param listeners - * @param event - */ - private void addNotificationTaskByScope( - final ImmutableList.Builder taskListBuilder, final ListenerTree.Node listeners, - final DOMImmutableDataChangeEvent event) { - DataChangeScope eventScope = event.getScope(); - for (DataChangeListenerRegistration listenerReg : listeners.getListeners()) { - DataChangeScope listenerScope = listenerReg.getScope(); - List> listenerSet = Collections - .> singletonList(listenerReg); - if (eventScope == DataChangeScope.BASE) { - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } else if (eventScope == DataChangeScope.ONE && listenerScope != DataChangeScope.BASE) { - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } else if (eventScope == DataChangeScope.SUBTREE && listenerScope == DataChangeScope.SUBTREE) { - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } - } - } + // Run through the tree + final ResolveDataChangeState s = ResolveDataChangeState.initial(candidate.getRootPath(), w.getRootNode()); + resolveAnyChangeEvent(s, candidate.getRootNode()); - /** - * - * Add notification tasks with merged event - * - * Separate Events by scope and creates merged notification tasks for each - * and every scope which is present. - * - * Adds merged events to task list based on scope requested by client. - * - * @param taskListBuilder - * @param listeners - * @param entries - */ - private void addNotificationTasksAndMergeEvents( - final ImmutableList.Builder taskListBuilder, final ListenerTree.Node listeners, - final Collection entries) { - - final Builder baseBuilder = builder(DataChangeScope.BASE); - final Builder oneBuilder = builder(DataChangeScope.ONE); - final Builder subtreeBuilder = builder(DataChangeScope.SUBTREE); - - boolean baseModified = false; - boolean oneModified = false; - boolean subtreeModified = false; - for (final DOMImmutableDataChangeEvent entry : entries) { - switch (entry.getScope()) { - // Absence of breaks is intentional here. Subtree contains base and - // one, one also contains base - case BASE: - baseBuilder.merge(entry); - baseModified = true; - case ONE: - oneBuilder.merge(entry); - oneModified = true; - case SUBTREE: - subtreeBuilder.merge(entry); - subtreeModified = true; + /* + * Convert to tasks, but be mindful of multiple values -- those indicate multiple + * wildcard matches, which need to be merged. + */ + final Collection ret = new ArrayList<>(); + for (Entry, Collection> e : collectedEvents.asMap().entrySet()) { + final Collection col = e.getValue(); + final DOMImmutableDataChangeEvent event; + + if (col.size() != 1) { + final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE); + for (DOMImmutableDataChangeEvent i : col) { + b.merge(i); + } + + event = b.build(); + LOG.trace("Merged events {} into event {}", col, event); + } else { + event = col.iterator().next(); + } + + ret.add(new ChangeListenerNotifyTask(e.getKey(), event, notificationMgr)); } - } - if (baseModified) { - addNotificationTaskExclusively(taskListBuilder, listeners, baseBuilder.build()); - } - if (oneModified) { - addNotificationTaskExclusively(taskListBuilder, listeners, oneBuilder.build()); - } - if (subtreeModified) { - addNotificationTaskExclusively(taskListBuilder, listeners, subtreeBuilder.build()); - } - } - - private void addNotificationTaskExclusively( - final ImmutableList.Builder taskListBuilder, final Node listeners, - final DOMImmutableDataChangeEvent event) { - for (DataChangeListenerRegistration listener : listeners.getListeners()) { - if (listener.getScope() == event.getScope()) { - Set> listenerSet = Collections - .> singleton(listener); - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } + // FIXME: so now we have tasks to submit tasks... Inception-style! + LOG.debug("Created tasks {}", ret); + return ret; } } @@ -245,94 +121,90 @@ final class ResolveDataChangeEventsTask implements Callable listeners, final DataTreeCandidateNode node) { - + private boolean resolveAnyChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode node) { if (node.getModificationType() != ModificationType.UNMODIFIED && !node.getDataAfter().isPresent() && !node.getDataBefore().isPresent()) { LOG.debug("Modification at {} has type {}, but no before- and after-data. Assuming unchanged.", - path, node.getModificationType()); - return NO_CHANGE; + state.getPath(), node.getModificationType()); + return false; } // no before and after state is present switch (node.getModificationType()) { case SUBTREE_MODIFIED: - return resolveSubtreeChangeEvent(path, listeners, node); + return resolveSubtreeChangeEvent(state, node); case MERGE: case WRITE: Preconditions.checkArgument(node.getDataAfter().isPresent(), - "Modification at {} has type {} but no after-data", path, node.getModificationType()); - if (node.getDataBefore().isPresent()) { - return resolveReplacedEvent(path, listeners, node.getDataBefore().get(), node.getDataAfter().get()); - } else { - return resolveCreateEvent(path, listeners, node.getDataAfter().get()); + "Modification at {} has type {} but no after-data", state.getPath(), node.getModificationType()); + if (!node.getDataBefore().isPresent()) { + resolveCreateEvent(state, node.getDataAfter().get()); + return true; } + + return resolveReplacedEvent(state, node.getDataBefore().get(), node.getDataAfter().get()); case DELETE: Preconditions.checkArgument(node.getDataBefore().isPresent(), - "Modification at {} has type {} but no before-data", path, node.getModificationType()); - return resolveDeleteEvent(path, listeners, node.getDataBefore().get()); + "Modification at {} has type {} but no before-data", state.getPath(), node.getModificationType()); + resolveDeleteEvent(state, node.getDataBefore().get()); + return true; case UNMODIFIED: - return NO_CHANGE; + return false; } - throw new IllegalStateException(String.format("Unhandled node state %s at %s", node.getModificationType(), path)); + throw new IllegalStateException(String.format("Unhandled node state %s at %s", node.getModificationType(), state.getPath())); } - private DOMImmutableDataChangeEvent resolveReplacedEvent(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode beforeData, - final NormalizedNode afterData) { - - // FIXME: BUG-1493: check the listeners to prune unneeded changes: - // for subtrees, we have to do all - // for one, we need to expand children - // for base, we just report replacement + private boolean resolveReplacedEvent(final ResolveDataChangeState state, + final NormalizedNode beforeData, final NormalizedNode afterData) { if (beforeData instanceof NormalizedNodeContainer) { - // Node is container (contains child) and we have interested - // listeners registered for it, that means we need to do - // resolution of changes on children level and can not - // shortcut resolution. - LOG.trace("Resolving subtree replace event for {} before {}, after {}",path,beforeData,afterData); + /* + * Node is a container (contains a child) and we have interested + * listeners registered for it, that means we need to do + * resolution of changes on children level and can not + * shortcut resolution. + */ + LOG.trace("Resolving subtree replace event for {} before {}, after {}", state.getPath(), beforeData, afterData); @SuppressWarnings("unchecked") NormalizedNodeContainer> beforeCont = (NormalizedNodeContainer>) beforeData; @SuppressWarnings("unchecked") NormalizedNodeContainer> afterCont = (NormalizedNodeContainer>) afterData; - return resolveNodeContainerReplaced(path, listeners, beforeCont, afterCont); - } else if (!beforeData.equals(afterData)) { - // Node is Leaf type (does not contain child nodes) - // so normal equals method is sufficient for determining change. - LOG.trace("Resolving leaf replace event for {} , before {}, after {}",path,beforeData,afterData); - DOMImmutableDataChangeEvent event = builder(DataChangeScope.BASE).setBefore(beforeData).setAfter(afterData) - .addUpdated(path, beforeData, afterData).build(); - addPartialTask(listeners, event); - return event; - } else { - return NO_CHANGE; + return resolveNodeContainerReplaced(state, beforeCont, afterCont); } + + // Node is a Leaf type (does not contain child nodes) + // so normal equals method is sufficient for determining change. + if (beforeData.equals(afterData)) { + LOG.trace("Skipping equal leaf {}", state.getPath()); + return false; + } + + LOG.trace("Resolving leaf replace event for {} , before {}, after {}", state.getPath(), beforeData, afterData); + DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE).addUpdated(state.getPath(), beforeData, afterData).build(); + state.addEvent(event); + state.collectEvents(beforeData, afterData, collectedEvents); + return true; } - private DOMImmutableDataChangeEvent resolveNodeContainerReplaced(final YangInstanceIdentifier path, - final Collection listeners, + private boolean resolveNodeContainerReplaced(final ResolveDataChangeState state, final NormalizedNodeContainer> beforeCont, final NormalizedNodeContainer> afterCont) { - final List childChanges = new LinkedList<>(); + if (!state.needsProcessing()) { + LOG.trace("Not processing replaced container {}", state.getPath()); + return true; + } // We look at all children from before and compare it with after state. + boolean childChanged = false; for (NormalizedNode beforeChild : beforeCont.getValue()) { final PathArgument childId = beforeChild.getIdentifier(); - YangInstanceIdentifier childPath = path.node(childId); - Collection childListeners = getListenerChildrenWildcarded(listeners, childId); - Optional> afterChild = afterCont.getChild(childId); - DOMImmutableDataChangeEvent childChange = resolveNodeContainerChildUpdated(childPath, childListeners, - beforeChild, afterChild); - // If change is empty (equals to NO_CHANGE) - if (childChange != NO_CHANGE) { - childChanges.add(childChange); + if (resolveNodeContainerChildUpdated(state.child(childId), beforeChild, afterCont.getChild(childId))) { + childChanged = true; } } @@ -345,187 +217,120 @@ final class ResolveDataChangeEventsTask implements Callable childListeners = getListenerChildrenWildcarded(listeners, childId); - YangInstanceIdentifier childPath = path.node(childId); - childChanges.add(resolveSameEventRecursivelly(childPath , childListeners, afterChild, - DOMImmutableDataChangeEvent.getCreateEventFactory())); + resolveSameEventRecursivelly(state.child(childId), afterChild, DOMImmutableDataChangeEvent.getCreateEventFactory()); + childChanged = true; } } - if (childChanges.isEmpty()) { - return NO_CHANGE; - } - Builder eventBuilder = builder(DataChangeScope.BASE) // - .setBefore(beforeCont) // - .setAfter(afterCont) - .addUpdated(path, beforeCont, afterCont); - for (DOMImmutableDataChangeEvent childChange : childChanges) { - eventBuilder.merge(childChange); + if (childChanged) { + DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE) + .addUpdated(state.getPath(), beforeCont, afterCont).build(); + state.addEvent(event); } - DOMImmutableDataChangeEvent replaceEvent = eventBuilder.build(); - addPartialTask(listeners, replaceEvent); - return replaceEvent; + state.collectEvents(beforeCont, afterCont, collectedEvents); + return childChanged; } - private DOMImmutableDataChangeEvent resolveNodeContainerChildUpdated(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode before, - final Optional> after) { - + private boolean resolveNodeContainerChildUpdated(final ResolveDataChangeState state, + final NormalizedNode before, final Optional> after) { if (after.isPresent()) { // REPLACE or SUBTREE Modified - return resolveReplacedEvent(path, listeners, before, after.get()); - - } else { - // AFTER state is not present - child was deleted. - return resolveSameEventRecursivelly(path, listeners, before, - DOMImmutableDataChangeEvent.getRemoveEventFactory()); + return resolveReplacedEvent(state, before, after.get()); } + + // AFTER state is not present - child was deleted. + resolveSameEventRecursivelly(state, before, DOMImmutableDataChangeEvent.getRemoveEventFactory()); + return true; } /** * Resolves create events deep down the interest listener tree. * - * * @param path * @param listeners * @param afterState * @return */ - private DOMImmutableDataChangeEvent resolveCreateEvent(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode afterState) { + private void resolveCreateEvent(final ResolveDataChangeState state, final NormalizedNode afterState) { @SuppressWarnings({ "unchecked", "rawtypes" }) final NormalizedNode node = (NormalizedNode) afterState; - return resolveSameEventRecursivelly(path, listeners, node, DOMImmutableDataChangeEvent.getCreateEventFactory()); + resolveSameEventRecursivelly(state, node, DOMImmutableDataChangeEvent.getCreateEventFactory()); } - private DOMImmutableDataChangeEvent resolveDeleteEvent(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode beforeState) { - + private void resolveDeleteEvent(final ResolveDataChangeState state, final NormalizedNode beforeState) { @SuppressWarnings({ "unchecked", "rawtypes" }) final NormalizedNode node = (NormalizedNode) beforeState; - return resolveSameEventRecursivelly(path, listeners, node, DOMImmutableDataChangeEvent.getRemoveEventFactory()); + resolveSameEventRecursivelly(state, node, DOMImmutableDataChangeEvent.getRemoveEventFactory()); } - private DOMImmutableDataChangeEvent resolveSameEventRecursivelly(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode node, - final SimpleEventFactory eventFactory) { - final DOMImmutableDataChangeEvent event = eventFactory.create(path, node); - DOMImmutableDataChangeEvent propagateEvent = event; + private void resolveSameEventRecursivelly(final ResolveDataChangeState state, + final NormalizedNode node, final SimpleEventFactory eventFactory) { + if (!state.needsProcessing()) { + LOG.trace("Skipping child {}", state.getPath()); + return; + } + // We have listeners for this node or it's children, so we will try // to do additional processing if (node instanceof NormalizedNodeContainer) { - LOG.trace("Resolving subtree recursive event for {}, type {}", path, eventFactory); - - Builder eventBuilder = builder(DataChangeScope.BASE); - eventBuilder.merge(event); - eventBuilder.setBefore(event.getOriginalSubtree()); - eventBuilder.setAfter(event.getUpdatedSubtree()); + LOG.trace("Resolving subtree recursive event for {}, type {}", state.getPath(), eventFactory); // Node has children, so we will try to resolve it's children // changes. @SuppressWarnings("unchecked") NormalizedNodeContainer> container = (NormalizedNodeContainer>) node; for (NormalizedNode child : container.getValue()) { - PathArgument childId = child.getIdentifier(); + final PathArgument childId = child.getIdentifier(); + LOG.trace("Resolving event for child {}", childId); - Collection childListeners = getListenerChildrenWildcarded(listeners, childId); - eventBuilder.merge(resolveSameEventRecursivelly(path.node(childId), childListeners, child, eventFactory)); + resolveSameEventRecursivelly(state.child(childId), child, eventFactory); } - propagateEvent = eventBuilder.build(); } - if (!listeners.isEmpty()) { - addPartialTask(listeners, propagateEvent); - } - return propagateEvent; - } - private DOMImmutableDataChangeEvent resolveSubtreeChangeEvent(final YangInstanceIdentifier path, - final Collection listeners, final DataTreeCandidateNode modification) { + final DOMImmutableDataChangeEvent event = eventFactory.create(state.getPath(), node); + LOG.trace("Adding event {} at path {}", event, state.getPath()); + state.addEvent(event); + state.collectEvents(event.getOriginalSubtree(), event.getUpdatedSubtree(), collectedEvents); + } - Preconditions.checkArgument(modification.getDataBefore().isPresent(), "Subtree change with before-data not present at path %s", path); - Preconditions.checkArgument(modification.getDataAfter().isPresent(), "Subtree change with after-data not present at path %s", path); + private boolean resolveSubtreeChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode modification) { + Preconditions.checkArgument(modification.getDataBefore().isPresent(), "Subtree change with before-data not present at path %s", state.getPath()); + Preconditions.checkArgument(modification.getDataAfter().isPresent(), "Subtree change with after-data not present at path %s", state.getPath()); - Builder one = builder(DataChangeScope.ONE). - setBefore(modification.getDataBefore().get()). - setAfter(modification.getDataAfter().get()); - Builder subtree = builder(DataChangeScope.SUBTREE). - setBefore(modification.getDataBefore().get()). - setAfter(modification.getDataAfter().get()); - boolean oneModified = false; + DataChangeScope scope = null; for (DataTreeCandidateNode childMod : modification.getChildNodes()) { - PathArgument childId = childMod.getIdentifier(); - YangInstanceIdentifier childPath = path.node(childId); - Collection childListeners = getListenerChildrenWildcarded(listeners, childId); - + final ResolveDataChangeState childState = state.child(childMod.getIdentifier()); switch (childMod.getModificationType()) { case WRITE: case MERGE: case DELETE: - one.merge(resolveAnyChangeEvent(childPath, childListeners, childMod)); - oneModified = true; + if (resolveAnyChangeEvent(childState, childMod)) { + scope = DataChangeScope.ONE; + } break; case SUBTREE_MODIFIED: - subtree.merge(resolveSubtreeChangeEvent(childPath, childListeners, childMod)); + if (resolveSubtreeChangeEvent(childState, childMod) && scope == null) { + scope = DataChangeScope.SUBTREE; + } break; case UNMODIFIED: // no-op break; } } - final DOMImmutableDataChangeEvent oneChangeEvent; - if(oneModified) { - one.addUpdated(path, modification.getDataBefore().get(), modification.getDataAfter().get()); - oneChangeEvent = one.build(); - subtree.merge(oneChangeEvent); - } else { - oneChangeEvent = null; - subtree.addUpdated(path, modification.getDataBefore().get(), modification.getDataAfter().get()); - } - DOMImmutableDataChangeEvent subtreeEvent = subtree.build(); - if (!listeners.isEmpty()) { - if(oneChangeEvent != null) { - addPartialTask(listeners, oneChangeEvent); - } - addPartialTask(listeners, subtreeEvent); - } - return subtreeEvent; - } - private DOMImmutableDataChangeEvent addPartialTask(final Collection listeners, - final DOMImmutableDataChangeEvent event) { - for (ListenerTree.Node listenerNode : listeners) { - if (!listenerNode.getListeners().isEmpty()) { - LOG.trace("Adding event {} for listeners {}",event,listenerNode); - events.put(listenerNode, event); - } - } - return event; - } + final NormalizedNode before = modification.getDataBefore().get(); + final NormalizedNode after = modification.getDataAfter().get(); - private static Collection getListenerChildrenWildcarded(final Collection parentNodes, - final PathArgument child) { - if (parentNodes.isEmpty()) { - return Collections.emptyList(); - } - com.google.common.collect.ImmutableList.Builder result = ImmutableList.builder(); - if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) { - NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType()); - addChildrenNodesToBuilder(result, parentNodes, wildcardedIdentifier); + if (scope != null) { + DOMImmutableDataChangeEvent one = DOMImmutableDataChangeEvent.builder(scope).addUpdated(state.getPath(), before, after).build(); + state.addEvent(one); } - addChildrenNodesToBuilder(result, parentNodes, child); - return result.build(); - } - private static void addChildrenNodesToBuilder(final ImmutableList.Builder result, - final Collection parentNodes, final PathArgument childIdentifier) { - for (ListenerTree.Node node : parentNodes) { - Optional child = node.getChild(childIdentifier); - if (child.isPresent()) { - result.add(child.get()); - } - } + state.collectEvents(before, after, collectedEvents); + return scope != null; } @SuppressWarnings("rawtypes") diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java new file mode 100644 index 0000000000..dca2eff705 --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder; +import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this + * method track which listeners are affected by a particular change node. It takes + * care of properly inheriting SUB/ONE listeners and also provides a means to + * understand when actual processing need not occur. + */ +final class ResolveDataChangeState { + private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class); + /** + * Inherited from all parents + */ + private final Iterable inheritedSub; + /** + * Inherited from immediate parent + */ + private final Iterable inheritedOne; + private final YangInstanceIdentifier nodeId; + private final Collection nodes; + + private final Map, Builder> subBuilders = new HashMap<>(); + private final Map, Builder> oneBuilders = new HashMap<>(); + private final Map, Builder> baseBuilders = new HashMap<>(); + + private ResolveDataChangeState(final YangInstanceIdentifier nodeId, + final Iterable inheritedSub, final Iterable inheritedOne, + final Collection nodes) { + this.nodeId = Preconditions.checkNotNull(nodeId); + this.nodes = Preconditions.checkNotNull(nodes); + this.inheritedSub = Preconditions.checkNotNull(inheritedSub); + this.inheritedOne = Preconditions.checkNotNull(inheritedOne); + + /* + * Collect the nodes which need to be propagated from us to the child. + */ + for (Node n : nodes) { + for (DataChangeListenerRegistration l : n.getListeners()) { + final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE); + switch (l.getScope()) { + case BASE: + baseBuilders.put(l, b); + break; + case ONE: + oneBuilders.put(l, b); + break; + case SUBTREE: + subBuilders.put(l, b); + break; + } + } + } + } + + /** + * Create an initial state handle at a particular root node. + * + * @param rootId root instance identifier + * @param root root node + * @return + */ + public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final Node root) { + return new ResolveDataChangeState(rootId, Collections.emptyList(), + Collections.emptyList(), Collections.singletonList(root)); + } + + /** + * Create a state handle for iterating over a particular child. + * + * @param childId ID of the child + * @return State handle + */ + public ResolveDataChangeState child(final PathArgument childId) { + return new ResolveDataChangeState(nodeId.node(childId), + Iterables.concat(inheritedSub, subBuilders.values()), + oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId)); + } + + /** + * Get the current path + * + * @return Current path. + */ + public YangInstanceIdentifier getPath() { + return nodeId; + } + + /** + * Check if this child needs processing. + * + * @return True if processing needs to occur, false otherwise. + */ + public boolean needsProcessing() { + // May have underlying listeners, so we need to process + if (!nodes.isEmpty()) { + return true; + } + // Have SUBTREE listeners + if (!Iterables.isEmpty(inheritedSub)) { + return true; + } + // Have ONE listeners + if (!Iterables.isEmpty(inheritedOne)) { + return true; + } + + // FIXME: do we need anything else? If not, flip this to 'false' + return true; + } + + /** + * Add an event to all current listeners. + * + * @param event + */ + public void addEvent(final DOMImmutableDataChangeEvent event) { + // Subtree builders get always notified + for (Builder b : subBuilders.values()) { + b.merge(event); + } + for (Builder b : inheritedSub) { + b.merge(event); + } + + if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) { + for (Builder b : oneBuilders.values()) { + b.merge(event); + } + } + + if (event.getScope() == DataChangeScope.BASE) { + for (Builder b : inheritedOne) { + b.merge(event); + } + for (Builder b : baseBuilders.values()) { + b.merge(event); + } + } + } + + /** + * Gather all non-empty events into the provided map. + * + * @param before before-image + * @param after after-image + * @param map target map + */ + public void collectEvents(final NormalizedNode before, final NormalizedNode after, + final Multimap, DOMImmutableDataChangeEvent> map) { + for (Entry, Builder> e : baseBuilders.entrySet()) { + final Builder b = e.getValue(); + if (!b.isEmpty()) { + map.put(e.getKey(), b.setBefore(before).setAfter(after).build()); + } + } + for (Entry, Builder> e : oneBuilders.entrySet()) { + final Builder b = e.getValue(); + if (!b.isEmpty()) { + map.put(e.getKey(), b.setBefore(before).setAfter(after).build()); + } + } + for (Entry, Builder> e : subBuilders.entrySet()) { + final Builder b = e.getValue(); + if (!b.isEmpty()) { + map.put(e.getKey(), b.setBefore(before).setAfter(after).build()); + } + } + + LOG.trace("Collected events {}", map); + } + + private static Collection getListenerChildrenWildcarded(final Collection parentNodes, + final PathArgument child) { + if (parentNodes.isEmpty()) { + return Collections.emptyList(); + } + + final List result = new ArrayList<>(); + if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) { + NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType()); + addChildNodes(result, parentNodes, wildcardedIdentifier); + } + addChildNodes(result, parentNodes, child); + return result; + } + + private static void addChildNodes(final List result, final Collection parentNodes, final PathArgument childIdentifier) { + for (Node node : parentNodes) { + Optional child = node.getChild(childIdentifier); + if (child.isPresent()) { + result.add(child.get()); + } + } + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java index 76a9354d1a..0e064cd504 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java @@ -7,8 +7,11 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.util.concurrent.MoreExecutors; + import java.util.Collection; import java.util.Map; + import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -36,8 +39,6 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContaine import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import com.google.common.util.concurrent.MoreExecutors; - public abstract class AbstractDataChangeListenerTest { protected static final YangInstanceIdentifier TOP_LEVEL = YangInstanceIdentifier @@ -74,6 +75,13 @@ public abstract class AbstractDataChangeListenerTest { } } + /** + * Create a new test task. The task will operate on the backed database, + * and will use the proper background executor service. + * + * @return Test task initialized to clean up {@value #TOP_LEVEL} and its + * children. + */ public final DatastoreTestTask newTestTask() { return new DatastoreTestTask(datastore, dclExecutorService).cleanup(DatastoreTestTask .simpleDelete(TOP_LEVEL)); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java index 84337de419..af58f63331 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java @@ -13,10 +13,18 @@ import org.junit.Test; import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +/** + * Base template for a test suite for testing DataChangeListener functionality. + */ public abstract class DefaultDataChangeListenerTestSuite extends AbstractDataChangeListenerTest { protected static final String FOO_SIBLING = "foo-sibling"; + /** + * Callback invoked when the test suite can modify task parameters. + * + * @param task Update task configuration as needed + */ abstract protected void customizeTask(DatastoreTestTask task); @Test diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java index cdf465aace..ddbba76ae0 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java @@ -34,8 +34,11 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite assertNotNull(change); - assertNotContains(change.getCreatedData(), TOP_LEVEL); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + /* + * Created data must not contain nested-list item, since that is two-level deep. + */ + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR)); + assertContains(change.getCreatedData(), path(FOO) ); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -48,11 +51,18 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite AsyncDataChangeEvent> change = task.getChangeEvent(); assertNotNull(change); - - assertContains(change.getCreatedData(), path(FOO, BAZ)); + /* + * Created data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getCreatedData(), path(FOO, BAZ)); assertContains(change.getUpdatedData(), path(FOO)); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO, BAR)); + /* + * Removed data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getRemovedPaths(), path(FOO, BAR)); } @@ -64,8 +74,9 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite assertNotNull(change); assertFalse(change.getCreatedData().isEmpty()); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); - assertNotContains(change.getCreatedData(), TOP_LEVEL); + // Base event should contain only changed item, no details about child. + assertContains(change.getCreatedData(), path(FOO)); + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ)); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -95,7 +106,12 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite assertEmpty(change.getUpdatedData()); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + /* + * Scope base listener event should contain top-level-list item and nested list path + * and should not contain baz, bar which are two-level deep + */ + assertContains(change.getRemovedPaths(), path(FOO)); + assertNotContains(change.getRemovedPaths(),path(FOO, BAZ),path(FOO,BAR)); } @Override diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java index 3407e0ffa4..75f9fce612 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java @@ -14,6 +14,7 @@ import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -34,8 +35,8 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { assertNotNull(change); - assertNotContains(change.getCreatedData(), TOP_LEVEL); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR)); + assertContains(change.getCreatedData(), path(FOO), path(FOO).node(NestedList.QNAME)); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -48,11 +49,18 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { AsyncDataChangeEvent> change = task.getChangeEvent(); assertNotNull(change); - - assertContains(change.getCreatedData(), path(FOO, BAZ)); - assertContains(change.getUpdatedData(), path(FOO)); + /* + * Created data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getCreatedData(), path(FOO, BAZ)); + assertContains(change.getUpdatedData(), path(FOO),path(FOO).node(NestedList.QNAME)); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO, BAR)); + /* + * Removed data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getRemovedPaths(), path(FOO, BAR)); } @@ -64,8 +72,9 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { assertNotNull(change); assertFalse(change.getCreatedData().isEmpty()); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); - assertNotContains(change.getCreatedData(), TOP_LEVEL); + // Base event should contain only changed item, and details about immediate child. + assertContains(change.getCreatedData(), path(FOO),path(FOO).node(NestedList.QNAME)); + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ)); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -96,7 +105,8 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { assertEmpty(change.getUpdatedData()); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + assertContains(change.getRemovedPaths(), path(FOO),path(FOO).node(NestedList.QNAME)); + assertNotContains(change.getRemovedPaths(), path(FOO, BAZ),path(FOO,BAR)); } @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java index 1a7d90e9c0..0999efff0f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java @@ -7,8 +7,8 @@ import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; - import com.google.common.collect.Sets; + import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -158,7 +158,7 @@ public final class NetconfSessionCapabilities { } // FIXME: do we really want to continue here? - moduleBasedCaps.add(QName.create(namespace, revision, moduleName)); + moduleBasedCaps.add(QName.cachedReference(QName.create(namespace, revision, moduleName))); nonModuleCaps.remove(capability); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java index e2ebcb2b25..76f5930457 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java @@ -60,6 +60,8 @@ public class RpcRegistry extends UntypedActor { public RpcRegistry() { bucketStore = getContext().actorOf(Props.create(BucketStore.class), "store"); + + log.info("Bucket store path = {}", bucketStore.path().toString()); } public RpcRegistry(ActorRef bucketStore) { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java index 2f634ce1fa..23cbaca32f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java @@ -15,6 +15,7 @@ import akka.actor.UntypedActor; import akka.cluster.Cluster; import akka.event.Logging; import akka.event.LoggingAdapter; +import org.opendaylight.controller.utils.ConditionalProbe; import java.util.HashMap; import java.util.Map; @@ -71,6 +72,8 @@ public class BucketStore extends UntypedActor { */ private ActorRef gossiper; + private ConditionalProbe probe; + public BucketStore(){ gossiper = getContext().actorOf(Props.create(Gossiper.class), "gossiper"); } @@ -88,27 +91,32 @@ public class BucketStore extends UntypedActor { @Override public void onReceive(Object message) throws Exception { - log.debug("Received message: node[{}], message[{}]", selfAddress, message); + log.debug("Received message: node[{}], message[{}]", selfAddress, + message); - if (message instanceof UpdateBucket) - receiveUpdateBucket(((UpdateBucket) message).getBucket()); + if (probe != null) { - else if (message instanceof GetAllBuckets) - receiveGetAllBucket(); + probe.tell(message, getSelf()); + } - else if (message instanceof GetLocalBucket) + if (message instanceof ConditionalProbe) { + log.info("Received probe {} {}", getSelf(), message); + probe = (ConditionalProbe) message; + } else if (message instanceof UpdateBucket) { + receiveUpdateBucket(((UpdateBucket) message).getBucket()); + } else if (message instanceof GetAllBuckets) { + receiveGetAllBucket(); + } else if (message instanceof GetLocalBucket) { receiveGetLocalBucket(); - - else if (message instanceof GetBucketsByMembers) - receiveGetBucketsByMembers(((GetBucketsByMembers) message).getMembers()); - - else if (message instanceof GetBucketVersions) + } else if (message instanceof GetBucketsByMembers) { + receiveGetBucketsByMembers( + ((GetBucketsByMembers) message).getMembers()); + } else if (message instanceof GetBucketVersions) { receiveGetBucketVersions(); - - else if (message instanceof UpdateRemoteBuckets) - receiveUpdateRemoteBuckets(((UpdateRemoteBuckets) message).getBuckets()); - - else { + } else if (message instanceof UpdateRemoteBuckets) { + receiveUpdateRemoteBuckets( + ((UpdateRemoteBuckets) message).getBuckets()); + } else { log.debug("Unhandled message [{}]", message); unhandled(message); } @@ -270,4 +278,5 @@ public class BucketStore extends UntypedActor { Address getSelfAddress() { return selfAddress; } + } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/utils/ConditionalProbe.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/utils/ConditionalProbe.java new file mode 100644 index 0000000000..13cec54175 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/utils/ConditionalProbe.java @@ -0,0 +1,32 @@ +/* + * 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.utils; + +import akka.actor.ActorRef; +import com.google.common.base.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConditionalProbe { + private final ActorRef actorRef; + private final Predicate predicate; + Logger log = LoggerFactory.getLogger(ConditionalProbe.class); + + public ConditionalProbe(ActorRef actorRef, Predicate predicate) { + this.actorRef = actorRef; + this.predicate = predicate; + } + + public void tell(Object message, ActorRef sender){ + if(predicate.apply(message)) { + log.info("sending message to probe {}", message); + actorRef.tell(message, sender); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java index da3942a828..e6793741a3 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java @@ -1,14 +1,16 @@ package org.opendaylight.controller.remote.rpc.registry; + import akka.actor.ActorPath; import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.ActorSystem; import akka.actor.ChildActorPath; import akka.actor.Props; -import akka.japi.Pair; import akka.testkit.JavaTestKit; +import com.google.common.base.Predicate; import com.typesafe.config.ConfigFactory; + import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -16,267 +18,269 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages; import org.opendaylight.controller.sal.connector.api.RpcRouter; +import org.opendaylight.controller.utils.ConditionalProbe; import org.opendaylight.yangtools.yang.common.QName; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; +import javax.annotation.Nullable; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRoutersReply; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; public class RpcRegistryTest { - private static ActorSystem node1; - private static ActorSystem node2; - private static ActorSystem node3; - - private ActorRef registry1; - private ActorRef registry2; - private ActorRef registry3; - - @BeforeClass - public static void setup() throws InterruptedException { - Thread.sleep(1000); //give some time for previous test to close netty ports - node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA")); - node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB")); - node3 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberC")); - } - - @AfterClass - public static void teardown(){ - JavaTestKit.shutdownActorSystem(node1); - JavaTestKit.shutdownActorSystem(node2); - JavaTestKit.shutdownActorSystem(node3); - if (node1 != null) - node1.shutdown(); - if (node2 != null) - node2.shutdown(); - if (node3 != null) - node3.shutdown(); - - } - - @Before - public void createRpcRegistry() throws InterruptedException { - registry1 = node1.actorOf(Props.create(RpcRegistry.class)); - registry2 = node2.actorOf(Props.create(RpcRegistry.class)); - registry3 = node3.actorOf(Props.create(RpcRegistry.class)); - } - - @After - public void stopRpcRegistry() throws InterruptedException { - if (registry1 != null) - node1.stop(registry1); - if (registry2 != null) - node2.stop(registry2); - if (registry3 != null) - node3.stop(registry3); - } + private static ActorSystem node1; + private static ActorSystem node2; + private static ActorSystem node3; + + private ActorRef registry1; + private ActorRef registry2; + private ActorRef registry3; + + @BeforeClass + public static void setup() throws InterruptedException { + node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA")); + node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB")); + node3 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberC")); + } + + @AfterClass + public static void teardown() { + JavaTestKit.shutdownActorSystem(node1); + JavaTestKit.shutdownActorSystem(node2); + JavaTestKit.shutdownActorSystem(node3); + if (node1 != null) + node1.shutdown(); + if (node2 != null) + node2.shutdown(); + if (node3 != null) + node3.shutdown(); + + } + + @Before + public void createRpcRegistry() throws InterruptedException { + registry1 = node1.actorOf(Props.create(RpcRegistry.class)); + registry2 = node2.actorOf(Props.create(RpcRegistry.class)); + registry3 = node3.actorOf(Props.create(RpcRegistry.class)); + } + + @After + public void stopRpcRegistry() throws InterruptedException { + if (registry1 != null) + node1.stop(registry1); + if (registry2 != null) + node2.stop(registry2); + if (registry3 != null) + node3.stop(registry3); + } + + /** + * One node cluster. + * 1. Register rpc, ensure router can be found + * 2. Then remove rpc, ensure its deleted + * + * @throws URISyntaxException + * @throws InterruptedException + */ + @Test + public void testAddRemoveRpcOnSameNode() throws URISyntaxException, InterruptedException { + validateSystemStartup(); + + final JavaTestKit mockBroker = new JavaTestKit(node1); + + final ActorPath bucketStorePath = new ChildActorPath(registry1.path(), "store"); + + //install probe + final JavaTestKit probe1 = createProbeForMessage( + node1, bucketStorePath, Messages.BucketStoreMessages.UpdateBucket.class); + + //Add rpc on node 1 + registry1.tell(new SetLocalRouter(mockBroker.getRef()), mockBroker.getRef()); + registry1.tell(getAddRouteMessage(), mockBroker.getRef()); + + //Bucket store should get an update bucket message. Updated bucket contains added rpc. + probe1.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateBucket.class); + + //Now remove rpc + registry1.tell(getRemoveRouteMessage(), mockBroker.getRef()); + + //Bucket store should get an update bucket message. Rpc is removed in the updated bucket + probe1.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateBucket.class); + + + } + + + /** + * Three node cluster. + * 1. Register rpc on 1 node, ensure 2nd node gets updated + * 2. Remove rpc on 1 node, ensure 2nd node gets updated + * + * @throws URISyntaxException + * @throws InterruptedException + */ + @Test + public void testRpcAddRemoveInCluster() throws URISyntaxException, InterruptedException { - /** - * One node cluster. - * 1. Register rpc, ensure router can be found - * 2. Then remove rpc, ensure its deleted - * - * @throws URISyntaxException - * @throws InterruptedException - */ - @Test - public void testAddRemoveRpcOnSameNode() throws URISyntaxException, InterruptedException { - - final JavaTestKit mockBroker = new JavaTestKit(node1); - - //Add rpc on node 1 - registry1.tell(new SetLocalRouter(mockBroker.getRef()), mockBroker.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker.getRef()); - - Thread.sleep(1000);// - - //find the route on node 1's registry - registry1.tell(new FindRouters(createRouteId()), mockBroker.getRef()); - FindRoutersReply message = mockBroker.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - List> pairs = message.getRouterWithUpdateTime(); - - validateRouterReceived(pairs, mockBroker.getRef()); - - //Now remove rpc - registry1.tell(getRemoveRouteMessage(), mockBroker.getRef()); - Thread.sleep(1000); - //find the route on node 1's registry - registry1.tell(new FindRouters(createRouteId()), mockBroker.getRef()); - message = mockBroker.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - pairs = message.getRouterWithUpdateTime(); - - Assert.assertTrue(pairs.isEmpty()); - } + validateSystemStartup(); + + final JavaTestKit mockBroker1 = new JavaTestKit(node1); + + //install probe on node2's bucket store + final ActorPath bucketStorePath = new ChildActorPath(registry2.path(), "store"); + final JavaTestKit probe2 = createProbeForMessage( + node2, bucketStorePath, Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - /** - * Three node cluster. - * 1. Register rpc on 1 node, ensure its router can be found on other 2. - * 2. Remove rpc on 1 node, ensure its removed on other 2. - * - * @throws URISyntaxException - * @throws InterruptedException - */ - @Test - public void testRpcAddRemoveInCluster() throws URISyntaxException, InterruptedException { - validateSystemStartup(); + //Add rpc on node 1 + registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); + registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - final JavaTestKit mockBroker1 = new JavaTestKit(node1); - final JavaTestKit mockBroker2 = new JavaTestKit(node2); - final JavaTestKit mockBroker3 = new JavaTestKit(node3); + //Bucket store on node2 should get a message to update its local copy of remote buckets + probe2.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //Add rpc on node 1 - registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); + //Now remove + registry1.tell(getRemoveRouteMessage(), mockBroker1.getRef()); - Thread.sleep(1000);// give some time for bucket store data sync + //Bucket store on node2 should get a message to update its local copy of remote buckets + probe2.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //find the route in node 2's registry - List> pairs = findRouters(registry2, mockBroker2); - validateRouterReceived(pairs, mockBroker1.getRef()); + } - //find the route in node 3's registry - pairs = findRouters(registry3, mockBroker3); - validateRouterReceived(pairs, mockBroker1.getRef()); + /** + * Three node cluster. + * Register rpc on 2 nodes. Ensure 3rd gets updated. + * + * @throws Exception + */ + @Test + public void testRpcAddedOnMultiNodes() throws Exception { - //Now remove - registry1.tell(getRemoveRouteMessage(), mockBroker1.getRef()); - Thread.sleep(1000);// give some time for bucket store data sync + validateSystemStartup(); - pairs = findRouters(registry2, mockBroker2); - Assert.assertTrue(pairs.isEmpty()); + final JavaTestKit mockBroker1 = new JavaTestKit(node1); + final JavaTestKit mockBroker2 = new JavaTestKit(node2); + final JavaTestKit mockBroker3 = new JavaTestKit(node3); - pairs = findRouters(registry3, mockBroker3); - Assert.assertTrue(pairs.isEmpty()); - } + registry3.tell(new SetLocalRouter(mockBroker3.getRef()), mockBroker3.getRef()); - /** - * Three node cluster. - * Register rpc on 2 nodes. Ensure 2 routers are found on 3rd. - * - * @throws Exception - */ - @Test - public void testAnRpcAddedOnMultiNodesShouldReturnMultiRouter() throws Exception { + //install probe on node 3 + final ActorPath bucketStorePath = new ChildActorPath(registry3.path(), "store"); + final JavaTestKit probe3 = createProbeForMessage( + node3, bucketStorePath, Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - validateSystemStartup(); - final JavaTestKit mockBroker1 = new JavaTestKit(node1); - final JavaTestKit mockBroker2 = new JavaTestKit(node2); - final JavaTestKit mockBroker3 = new JavaTestKit(node3); + //Add rpc on node 1 + registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); + registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - //Thread.sleep(5000);//let system come up + probe3.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //Add rpc on node 1 - registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - //Add same rpc on node 2 - registry2.tell(new SetLocalRouter(mockBroker2.getRef()), mockBroker2.getRef()); - registry2.tell(getAddRouteMessage(), mockBroker2.getRef()); + //Add same rpc on node 2 + registry2.tell(new SetLocalRouter(mockBroker2.getRef()), mockBroker2.getRef()); + registry2.tell(getAddRouteMessage(), mockBroker2.getRef()); - registry3.tell(new SetLocalRouter(mockBroker3.getRef()), mockBroker3.getRef()); - Thread.sleep(1000);// give some time for bucket store data sync + probe3.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + } - //find the route in node 3's registry - registry3.tell(new FindRouters(createRouteId()), mockBroker3.getRef()); - FindRoutersReply message = mockBroker3.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - List> pairs = message.getRouterWithUpdateTime(); + private JavaTestKit createProbeForMessage(ActorSystem node, ActorPath subjectPath, final Class clazz) { + final JavaTestKit probe = new JavaTestKit(node); - validateMultiRouterReceived(pairs, mockBroker1.getRef(), mockBroker2.getRef()); + ConditionalProbe conditionalProbe = + new ConditionalProbe(probe.getRef(), new Predicate() { + @Override + public boolean apply(@Nullable Object input) { + return clazz.equals(input.getClass()); + } + }); - } + ActorSelection subject = node.actorSelection(subjectPath); + subject.tell(conditionalProbe, ActorRef.noSender()); - private List> findRouters(ActorRef registry, JavaTestKit receivingActor) throws URISyntaxException { - registry.tell(new FindRouters(createRouteId()), receivingActor.getRef()); - FindRoutersReply message = receivingActor.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - return message.getRouterWithUpdateTime(); - } + return probe; - private void validateMultiRouterReceived(List> actual, ActorRef... expected) { - Assert.assertTrue(actual != null); - Assert.assertTrue(actual.size() == expected.length); - } + } - private void validateRouterReceived(List> actual, ActorRef expected){ - Assert.assertTrue(actual != null); - Assert.assertTrue(actual.size() == 1); + private void validateSystemStartup() throws InterruptedException { - for (Pair pair : actual){ - Assert.assertTrue(expected.path().uid() == pair.first().path().uid()); - } - } + ActorPath gossiper1Path = new ChildActorPath(new ChildActorPath(registry1.path(), "store"), "gossiper"); + ActorPath gossiper2Path = new ChildActorPath(new ChildActorPath(registry2.path(), "store"), "gossiper"); + ActorPath gossiper3Path = new ChildActorPath(new ChildActorPath(registry3.path(), "store"), "gossiper"); - private void validateSystemStartup() throws InterruptedException { + ActorSelection gossiper1 = node1.actorSelection(gossiper1Path); + ActorSelection gossiper2 = node2.actorSelection(gossiper2Path); + ActorSelection gossiper3 = node3.actorSelection(gossiper3Path); - Thread.sleep(5000); - ActorPath gossiper1Path = new ChildActorPath(new ChildActorPath(registry1.path(), "store"), "gossiper"); - ActorPath gossiper2Path = new ChildActorPath(new ChildActorPath(registry2.path(), "store"), "gossiper"); - ActorPath gossiper3Path = new ChildActorPath(new ChildActorPath(registry3.path(), "store"), "gossiper"); - ActorSelection gossiper1 = node1.actorSelection(gossiper1Path); - ActorSelection gossiper2 = node2.actorSelection(gossiper2Path); - ActorSelection gossiper3 = node3.actorSelection(gossiper3Path); + if (!resolveReference(gossiper1, gossiper2, gossiper3)) + Assert.fail("Could not find gossipers"); + } + private Boolean resolveReference(ActorSelection... gossipers) { - if (!resolveReference(gossiper1, gossiper2, gossiper3)) - Assert.fail("Could not find gossipers"); - } + Boolean resolved = true; + for (int i = 0; i < 5; i++) { - private Boolean resolveReference(ActorSelection... gossipers) throws InterruptedException { + resolved = true; + System.out.println(System.currentTimeMillis() + " Resolving gossipers; trial #" + i); - Boolean resolved = true; + for (ActorSelection gossiper : gossipers) { + ActorRef ref = null; - for (int i=0; i< 5; i++) { - Thread.sleep(1000); - for (ActorSelection gossiper : gossipers) { - Future future = gossiper.resolveOne(new FiniteDuration(5000, TimeUnit.MILLISECONDS)); + try { + Future future = gossiper.resolveOne(new FiniteDuration(15000, TimeUnit.MILLISECONDS)); + ref = Await.result(future, new FiniteDuration(10000, TimeUnit.MILLISECONDS)); + } catch (Exception e) { + System.out.println("Could not find gossiper in attempt#" + i + ". Got exception " + e.getMessage()); + } - ActorRef ref = null; - try { - ref = Await.result(future, new FiniteDuration(10000, TimeUnit.MILLISECONDS)); - } catch (Exception e) { - e.printStackTrace(); - } + if (ref == null) + resolved = false; + } - if (ref == null) - resolved = false; - } + if (resolved) break; - if (resolved) break; - } - return resolved; } + return resolved; + } - private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { - return new AddOrUpdateRoutes(createRouteIds()); - } + private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { + return new AddOrUpdateRoutes(createRouteIds()); + } - private RemoveRoutes getRemoveRouteMessage() throws URISyntaxException { - return new RemoveRoutes(createRouteIds()); - } + private RemoveRoutes getRemoveRouteMessage() throws URISyntaxException { + return new RemoveRoutes(createRouteIds()); + } - private List> createRouteIds() throws URISyntaxException { - QName type = new QName(new URI("/mockrpc"), "mockrpc"); - List> routeIds = new ArrayList<>(); - routeIds.add(new RouteIdentifierImpl(null, type, null)); - return routeIds; - } + private List> createRouteIds() throws URISyntaxException { + QName type = new QName(new URI("/mockrpc"), "mockrpc"); + List> routeIds = new ArrayList<>(); + routeIds.add(new RouteIdentifierImpl(null, type, null)); + return routeIds; + } - private RpcRouter.RouteIdentifier createRouteId() throws URISyntaxException { - QName type = new QName(new URI("/mockrpc"), "mockrpc"); - return new RouteIdentifierImpl(null, type, null); - } -} \ No newline at end of file +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf index 61fab7e0fe..b578d6f04f 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf @@ -1,6 +1,6 @@ odl-cluster{ akka { - loglevel = "INFO" + loglevel = "DEBUG" #log-config-on-start = on actor { @@ -45,6 +45,9 @@ memberA{ loggers = ["akka.event.slf4j.Slf4jLogger"] actor { provider = "akka.cluster.ClusterActorRefProvider" + debug { + #lifecycle = on + } } remote { log-received-messages = off diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/logback.xml b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/logback.xml new file mode 100644 index 0000000000..5246f01d05 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java b/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java index d7ce9485c6..451cad4816 100644 --- a/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java +++ b/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java @@ -91,7 +91,7 @@ class FlowCapableTopologyExporter implements FlowTopologyDiscoveryListener, Open public void applyOperation(final ReadWriteTransaction transaction) { final Node node = toTopologyNode(toTopologyNodeId(notification.getId()), notification.getNodeRef()); final InstanceIdentifier path = getNodePath(toTopologyNodeId(notification.getId())); - transaction.put(LogicalDatastoreType.OPERATIONAL, path, node); + transaction.merge(LogicalDatastoreType.OPERATIONAL, path, node, true); } }); } @@ -130,7 +130,7 @@ class FlowCapableTopologyExporter implements FlowTopologyDiscoveryListener, Open TerminationPoint point = toTerminationPoint(toTerminationPointId(notification.getId()), notification.getNodeConnectorRef()); final InstanceIdentifier path = tpPath(nodeId, point.getKey().getTpId()); - transaction.put(LogicalDatastoreType.OPERATIONAL, path, point); + transaction.merge(LogicalDatastoreType.OPERATIONAL, path, point, true); if ((fcncu.getState() != null && fcncu.getState().isLinkDown()) || (fcncu.getConfiguration() != null && fcncu.getConfiguration().isPORTDOWN())) { removeAffectedLinks(point.getTpId()); diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java index 004a22f694..bd91af5bcc 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java @@ -9,7 +9,9 @@ package org.opendaylight.controller.netconf.client; import io.netty.channel.Channel; + import java.util.Collection; + import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSession; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder; @@ -24,8 +26,16 @@ public class NetconfClientSession extends AbstractNetconfSession capabilities; - public NetconfClientSession(NetconfClientSessionListener sessionListener, Channel channel, long sessionId, - Collection capabilities) { + /** + * Construct a new session. + * + * @param sessionListener + * @param channel + * @param sessionId + * @param capabilities set of advertised capabilities. Expected to be immutable. + */ + public NetconfClientSession(final NetconfClientSessionListener sessionListener, final Channel channel, final long sessionId, + final Collection capabilities) { super(sessionListener, channel, sessionId); this.capabilities = capabilities; logger.debug("Client Session {} created", toString()); @@ -41,7 +51,7 @@ public class NetconfClientSession extends AbstractNetconfSession promise, - Channel channel, - Timer timer, - NetconfClientSessionListener sessionListener, - long connectionTimeoutMillis) { + protected NetconfClientSessionNegotiator(final NetconfClientSessionPreferences sessionPreferences, + final Promise promise, + final Channel channel, + final Timer timer, + final NetconfClientSessionListener sessionListener, + final long connectionTimeoutMillis) { super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis); } @Override - protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException { + protected void handleMessage(final NetconfHelloMessage netconfMessage) throws NetconfDocumentedException { final NetconfClientSession session = getSessionForHelloMessage(netconfMessage); replaceHelloMessageInboundHandler(session); @@ -98,7 +100,7 @@ public class NetconfClientSessionNegotiator extends }); } - private boolean shouldUseExi(NetconfHelloMessage helloMsg) { + private boolean shouldUseExi(final NetconfHelloMessage helloMsg) { return containsExi10Capability(helloMsg.getDocument()) && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument()); } @@ -113,7 +115,7 @@ public class NetconfClientSessionNegotiator extends return false; } - private long extractSessionId(Document doc) { + private long extractSessionId(final Document doc) { final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE); String textContent = sessionIdNode.getTextContent(); if (textContent == null || textContent.equals("")) { @@ -124,10 +126,14 @@ public class NetconfClientSessionNegotiator extends } @Override - protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, - NetconfHelloMessage message) throws NetconfDocumentedException { + protected NetconfClientSession getSession(final NetconfClientSessionListener sessionListener, final Channel channel, + final NetconfHelloMessage message) throws NetconfDocumentedException { long sessionId = extractSessionId(message.getDocument()); - Collection capabilities = NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument()); + + // Copy here is important: it disconnects the strings from the document + Collection capabilities = ImmutableList.copyOf(NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument())); + + // FIXME: scalability: we could instantiate a cache to share the same collections return new NetconfClientSession(sessionListener, channel, sessionId, capabilities); } @@ -138,15 +144,15 @@ public class NetconfClientSessionNegotiator extends private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler"; private final NetconfClientSession session; - private NetconfStartExiMessage startExiMessage; + private final NetconfStartExiMessage startExiMessage; - ExiConfirmationInboundHandler(NetconfClientSession session, final NetconfStartExiMessage startExiMessage) { + ExiConfirmationInboundHandler(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) { this.session = session; this.startExiMessage = startExiMessage; } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { ctx.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER); NetconfMessage netconfMessage = (NetconfMessage) msg; diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java index 1a2eb3f1ab..4ca7bdf958 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java @@ -41,7 +41,7 @@ class SshClientAdapter implements Runnable { private OutputStream stdIn; - private Queue postponed = new LinkedList<>(); + private final Queue postponed = new LinkedList<>(); private ChannelHandlerContext ctx; private ChannelPromise disconnectPromise; @@ -50,42 +50,36 @@ class SshClientAdapter implements Runnable { private final Object lock = new Object(); - public SshClientAdapter(SshClient sshClient, Invoker invoker) { + public SshClientAdapter(final SshClient sshClient, final Invoker invoker) { this.sshClient = sshClient; this.invoker = invoker; } - // TODO: refactor + // TODO ganymed spawns a Thread that receives the data from remote inside TransportManager + // Get rid of this thread and reuse Ganymed internal thread (not sure if its possible without modifications in ganymed) public void run() { try { - SshSession session = sshClient.openSession(); + final SshSession session = sshClient.openSession(); invoker.invoke(session); - InputStream stdOut = session.getStdout(); - session.getStderr(); + final InputStream stdOut = session.getStdout(); synchronized (lock) { - stdIn = session.getStdin(); - ByteBuf message; - while ((message = postponed.poll()) != null) { - writeImpl(message); + while (postponed.peek() != null) { + writeImpl(postponed.poll()); } } while (!stopRequested.get()) { - byte[] readBuff = new byte[BUFFER_SIZE]; - int c = stdOut.read(readBuff); + final byte[] readBuff = new byte[BUFFER_SIZE]; + final int c = stdOut.read(readBuff); if (c == -1) { continue; } - byte[] tranBuff = new byte[c]; - System.arraycopy(readBuff, 0, tranBuff, 0, c); - ByteBuf byteBuf = Unpooled.buffer(c); - byteBuf.writeBytes(tranBuff); - ctx.fireChannelRead(byteBuf); + ctx.fireChannelRead(Unpooled.copiedBuffer(readBuff, 0, c)); } - } catch (Exception e) { + } catch (final Exception e) { logger.error("Unexpected exception", e); } finally { sshClient.close(); @@ -99,7 +93,7 @@ class SshClientAdapter implements Runnable { } // TODO: needs rework to match netconf framer API. - public void write(ByteBuf message) throws IOException { + public void write(final ByteBuf message) throws IOException { synchronized (lock) { if (stdIn == null) { postponed.add(message); @@ -109,28 +103,28 @@ class SshClientAdapter implements Runnable { } } - private void writeImpl(ByteBuf message) throws IOException { + private void writeImpl(final ByteBuf message) throws IOException { message.getBytes(0, stdIn, message.readableBytes()); message.release(); stdIn.flush(); } - public void stop(ChannelPromise promise) { + public void stop(final ChannelPromise promise) { synchronized (lock) { stopRequested.set(true); disconnectPromise = promise; } } - public Thread start(ChannelHandlerContext ctx, ChannelFuture channelFuture) { + public Thread start(final ChannelHandlerContext ctx, final ChannelFuture channelFuture) { checkArgument(channelFuture.isSuccess()); checkNotNull(ctx.channel().remoteAddress()); synchronized (this) { checkState(this.ctx == null); this.ctx = ctx; } - String threadName = toString(); - Thread thread = new Thread(this, threadName); + final String threadName = toString(); + final Thread thread = new Thread(this, threadName); thread.start(); return thread; } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java index 44893b8794..9cdc5926f0 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java @@ -9,8 +9,6 @@ package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client; import ch.ethz.ssh2.Session; -import ch.ethz.ssh2.StreamGobbler; - import ch.ethz.ssh2.channel.Channel; import java.io.Closeable; import java.io.IOException; @@ -23,19 +21,20 @@ import java.io.OutputStream; class SshSession implements Closeable { private final Session session; - public SshSession(Session session) { + public SshSession(final Session session) { this.session = session; } - - public void startSubSystem(String name) throws IOException { + public void startSubSystem(final String name) throws IOException { session.startSubSystem(name); } public InputStream getStdout() { - return new StreamGobbler(session.getStdout()); + return session.getStdout(); } + // FIXME according to http://www.ganymed.ethz.ch/ssh2/FAQ.html#blocking you should read data from both stdout and stderr to prevent window filling up (since stdout and stderr share a window) + // FIXME stdErr is not used anywhere public InputStream getStderr() { return session.getStderr(); } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java index 6300c56e72..3fffbb2d2c 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java @@ -243,6 +243,12 @@ class ServerConnectionCallbackImpl implements ServerConnectionCallback { ChannelFuture clientChannelFuture = initializeNettyConnection(localAddress, bossGroup, sshClientHandler); // get channel final Channel channel = clientChannelFuture.awaitUninterruptibly().channel(); + + // write additional header before polling thread is started + // polling thread could process and forward data before additional header is written + // This will result into unexpected state: hello message without additional header and the next message with additional header + channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes())); + new ClientInputStreamPoolingThread(session, ss.getStdout(), channel, new AutoCloseable() { @Override public void close() throws Exception { @@ -259,9 +265,6 @@ class ServerConnectionCallbackImpl implements ServerConnectionCallback { } } }, sshClientHandler.getChannelHandlerContext()).start(); - - // write additional header - channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes())); } else { logger.debug("{} Wrong subsystem requested:'{}', closing ssh session", serverSession, subsystem); String reason = "Only netconf subsystem is supported, requested:" + subsystem; diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java index 59e9f4c980..7a4c10e23b 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java @@ -19,6 +19,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.List; +import java.util.concurrent.TimeUnit; import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.annotation.Arg; import net.sourceforge.argparse4j.inf.ArgumentParser; @@ -49,6 +50,9 @@ public final class Main { @Arg(dest = "starting-port") public int startingPort; + @Arg(dest = "generate-config-connection-timeout") + public int generateConfigsTimeout; + @Arg(dest = "generate-configs-dir") public File generateConfigsDir; @@ -58,6 +62,9 @@ public final class Main { @Arg(dest = "ssh") public boolean ssh; + @Arg(dest = "exi") + public boolean exi; + static ArgumentParser getParser() { final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testool"); parser.addArgument("--devices-count") @@ -79,6 +86,12 @@ public final class Main { .help("First port for simulated device. Each other device will have previous+1 port number") .dest("starting-port"); + parser.addArgument("--generate-config-connection-timeout") + .type(Integer.class) + .setDefault((int)TimeUnit.MINUTES.toMillis(5)) + .help("Timeout to be generated in initial config files") + .dest("generate-config-connection-timeout"); + parser.addArgument("--generate-configs-batch-size") .type(Integer.class) .setDefault(100) @@ -96,6 +109,12 @@ public final class Main { .help("Whether to use ssh for transport or just pure tcp") .dest("ssh"); + parser.addArgument("--exi") + .type(Boolean.class) + .setDefault(false) + .help("Whether to use exi to transport xml content") + .dest("exi"); + return parser; } @@ -110,6 +129,8 @@ public final class Main { } public static void main(final String[] args) { + ch.ethz.ssh2.log.Logger.enabled = true; + final Params params = parseArgs(args, Params.getParser()); params.validate(); @@ -117,7 +138,7 @@ public final class Main { try { final List openDevices = netconfDeviceSimulator.start(params); if(params.generateConfigsDir != null) { - new ConfigGenerator(params.generateConfigsDir, openDevices).generate(params.ssh, params.generateConfigBatchSize); + new ConfigGenerator(params.generateConfigsDir, openDevices).generate(params.ssh, params.generateConfigBatchSize, params.generateConfigsTimeout); } } catch (final Exception e) { LOG.error("Unhandled exception", e); @@ -164,7 +185,7 @@ public final class Main { this.openDevices = openDevices; } - public void generate(final boolean useSsh, final int batchSize) { + public void generate(final boolean useSsh, final int batchSize, final int generateConfigsTimeout) { if(directory.exists() == false) { checkState(directory.mkdirs(), "Unable to create folder %s" + directory); } @@ -182,7 +203,7 @@ public final class Main { configBlueprint = configBlueprint.replace(NETCONF_USE_SSH, "%s"); final String before = configBlueprint.substring(0, configBlueprint.indexOf("")); - final String middleBlueprint = configBlueprint.substring(configBlueprint.indexOf(""), configBlueprint.indexOf("") + "".length()); + final String middleBlueprint = configBlueprint.substring(configBlueprint.indexOf(""), configBlueprint.indexOf("")); final String after = configBlueprint.substring(configBlueprint.indexOf("") + "".length()); int connectorCount = 0; @@ -196,7 +217,9 @@ public final class Main { } final String name = String.valueOf(openDevice) + SIM_DEVICE_SUFFIX; - final String configContent = String.format(middleBlueprint, name, String.valueOf(openDevice), String.valueOf(!useSsh)); + String configContent = String.format(middleBlueprint, name, String.valueOf(openDevice), String.valueOf(!useSsh)); + configContent = String.format("%s%s%d%s\n%s\n", configContent, "", generateConfigsTimeout, "", ""); + b.append(configContent); connectorCount++; if(connectorCount == batchSize) { diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java index b21c02ac35..3a52f0a85e 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java @@ -24,7 +24,6 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.net.Inet4Address; @@ -42,6 +41,7 @@ import java.util.concurrent.ExecutionException; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory; @@ -91,7 +91,7 @@ public class NetconfDeviceSimulator implements Closeable { this.hashedWheelTimer = hashedWheelTimer; } - private NetconfServerDispatcher createDispatcher(final Map moduleBuilders) { + private NetconfServerDispatcher createDispatcher(final Map moduleBuilders, final boolean exi) { final Set capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function() { @Override @@ -108,8 +108,12 @@ public class NetconfDeviceSimulator implements Closeable { final DefaultCommitNotificationProducer commitNotifier = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + final Set serverCapabilities = exi + ? NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES + : Sets.newHashSet(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1); + final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( - hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService()); + hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService(), serverCapabilities); final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( serverNegotiatorFactory); @@ -147,7 +151,7 @@ public class NetconfDeviceSimulator implements Closeable { public List start(final Main.Params params) { final Map moduleBuilders = parseSchemasToModuleBuilders(params); - final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders); + final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi); int currentPort = params.startingPort;