From: adetalhouet Date: Mon, 18 Jan 2016 21:12:14 +0000 (-0500) Subject: Bug 4940 - correctly implement default-request-timeout-millis X-Git-Tag: release/lithium-sr4~14 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=7f256c9b9d7b5fb2ed6aab936781ce09ec165035;p=controller.git Bug 4940 - correctly implement default-request-timeout-millis Change-Id: I6922839d08459e3fd5225ff08213daf646608eaf Signed-off-by: adetalhouet --- diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml index b60b967414..d4195f4e30 100644 --- a/opendaylight/md-sal/sal-netconf-connector/pom.xml +++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml @@ -179,6 +179,18 @@ mockito-all test + + org.powermock + powermock-api-mockito + 1.5.2 + test + + + org.powermock + powermock-module-junit4 + 1.5.2 + test + diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index 20221d788c..be59580cb3 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -96,8 +96,8 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co checkNotNull(getConnectionTimeoutMillis(), connectionTimeoutMillisJmxAttribute); checkCondition(getConnectionTimeoutMillis() > 0, "must be > 0", connectionTimeoutMillisJmxAttribute); - checkNotNull(getConnectionTimeoutMillis(), defaultRequestTimeoutMillisJmxAttribute); - checkCondition(getConnectionTimeoutMillis() > 0, "must be > 0", defaultRequestTimeoutMillisJmxAttribute); + checkNotNull(getDefaultRequestTimeoutMillis(), defaultRequestTimeoutMillisJmxAttribute); + checkCondition(getDefaultRequestTimeoutMillis() > 0, "must be > 0", defaultRequestTimeoutMillisJmxAttribute); checkNotNull(getBetweenAttemptsTimeoutMillis(), betweenAttemptsTimeoutMillisJmxAttribute); checkCondition(getBetweenAttemptsTimeoutMillis() > 0, "must be > 0", betweenAttemptsTimeoutMillisJmxAttribute); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java index bc6adc6a1f..1c32bbaa4b 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java @@ -54,7 +54,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker { @Override public DOMDataReadOnlyTransaction newReadOnlyTransaction() { - return new ReadOnlyTx(netconfOps, id); + return new ReadOnlyTx(netconfOps, id, requestTimeoutMillis); } @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java index 93efc18b21..cdc7a53253 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java @@ -3,10 +3,13 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; + import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; + import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; @@ -20,6 +23,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +31,7 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { private static final Logger LOG = LoggerFactory.getLogger(AbstractWriteTx.class); - private final long defaultRequestTimeoutMillis; + protected final long defaultRequestTimeoutMillis; protected final RemoteDeviceId id; protected final NetconfBaseOps netOps; protected final boolean rollbackSupport; @@ -56,7 +60,7 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { protected void invokeBlocking(final String msg, final Function> op) throws NetconfDocumentedException { try { - final DOMRpcResult compositeNodeRpcResult = op.apply(netOps).get(defaultRequestTimeoutMillis, TimeUnit.MILLISECONDS); + final DOMRpcResult compositeNodeRpcResult = op.apply(netOps).get(); if(isSuccess(compositeNodeRpcResult) == false) { throw new NetconfDocumentedException(id + ": " + msg + " failed: " + compositeNodeRpcResult.getErrors(), NetconfDocumentedException.ErrorType.application, NetconfDocumentedException.ErrorTag.operation_failed, NetconfDocumentedException.ErrorSeverity.warning); @@ -64,7 +68,7 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { } catch (final InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); - } catch (final ExecutionException | TimeoutException e) { + } catch (final ExecutionException e) { throw new NetconfDocumentedException(id + ": " + msg + " failed: " + e.getMessage(), e, NetconfDocumentedException.ErrorType.application, NetconfDocumentedException.ErrorTag.operation_failed, NetconfDocumentedException.ErrorSeverity.warning); } @@ -166,4 +170,17 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { } protected abstract void editConfig(DataContainerChild editStructure, Optional defaultOperation) throws NetconfDocumentedException; + + protected ListenableFuture perfomRequestWithTimeout(String operation, ListenableFuture future) { + try { + future.get(defaultRequestTimeoutMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException e) { + LOG.error("{}: {} failed with error", operation, id, e); + return Futures.immediateFailedCheckedFuture(new RuntimeException(id + ": " + operation + " failed")); + } catch (TimeoutException e) { + LOG.warn("{}: Unable to {} after {} milliseconds", id, operation, defaultRequestTimeoutMillis, e); + return Futures.immediateFailedCheckedFuture(new SchemaSourceException(e.getMessage())); + } + return future; + } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java index e08731ed40..4200c6c137 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTx.java @@ -14,7 +14,11 @@ import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; + import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; @@ -40,9 +44,12 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { private final RemoteDeviceId id; private final FutureCallback loggingCallback; - public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id) { + private final long requestTimeoutMillis; + + public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id, final long requestTimeoutMillis) { this.netconfOps = netconfOps; this.id = id; + this.requestTimeoutMillis = requestTimeoutMillis; // Simple logging callback to log result of read operation loggingCallback = new FutureCallback() { @@ -53,7 +60,6 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { } else { LOG.warn("{}: Reading data unsuccessful: {}", id, result.getErrors()); } - } @Override @@ -77,6 +83,11 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { } }); + + if(!readWithTimeout("readConfigurationData", configRunning)) { + return null; + } + return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER); } @@ -108,6 +119,10 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { } }); + if(!readWithTimeout("readOperationalData", configCandidate)) { + return null; + } + return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER); } @@ -146,4 +161,18 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction { public Object getIdentifier() { return this; } + + private boolean readWithTimeout(String operation, ListenableFuture future) { + try { + future.get(requestTimeoutMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException e) { + LOG.error("{}: {} failed with error", id, operation, e); + throw new RuntimeException(id + ": readOperationalData failed"); + } catch (TimeoutException e) { + LOG.warn("{}: Unable to {} after {} milliseconds", id, operation, requestTimeoutMillis, e); + future.cancel(true); + return false; + } + return true; + } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java index fbd99c5128..403df18fbf 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java @@ -8,8 +8,10 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx; + import com.google.common.base.Function; import com.google.common.util.concurrent.ListenableFuture; + import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; @@ -29,7 +31,7 @@ public class WriteCandidateRunningTx extends WriteCandidateTx { private static final Logger LOG = LoggerFactory.getLogger(WriteCandidateRunningTx.class); - public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final boolean rollbackSupport, long requestTimeoutMillis) { + public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final boolean rollbackSupport, final long requestTimeoutMillis) { super(id, netOps, rollbackSupport, requestTimeoutMillis); } @@ -46,11 +48,13 @@ public class WriteCandidateRunningTx extends WriteCandidateTx { } private void lockRunning() { + final String operation = "Lock Running"; try { - invokeBlocking("Lock running", new Function>() { + invokeBlocking(operation, new Function>() { @Override public ListenableFuture apply(final NetconfBaseOps input) { - return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id)); + return perfomRequestWithTimeout(operation, input.lockRunning(new NetconfRpcFutureCallback(operation, id))); + } }); } catch (final NetconfDocumentedException e) { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java index 0f2b00280a..4008b4215a 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteCandidateTx.java @@ -14,6 +14,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; + import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; @@ -93,11 +94,12 @@ public class WriteCandidateTx extends AbstractWriteTx { } private void lock() throws NetconfDocumentedException { + final String operation = "Lock candidate"; try { - invokeBlocking("Lock candidate", new Function>() { + invokeBlocking(operation, new Function>() { @Override public ListenableFuture apply(final NetconfBaseOps input) { - return input.lockCandidate(new NetconfRpcFutureCallback("Lock candidate", id)); + return perfomRequestWithTimeout(operation, input.lockCandidate(new NetconfRpcFutureCallback(operation, id))); } }); } catch (final NetconfDocumentedException e) { @@ -183,14 +185,16 @@ public class WriteCandidateTx extends AbstractWriteTx { @Override protected void editConfig(final DataContainerChild editStructure, final Optional defaultOperation) throws NetconfDocumentedException { - invokeBlocking("Edit candidate", new Function>() { + final String operation = "Edit candidate"; + invokeBlocking(operation, new Function>() { @Override public ListenableFuture apply(final NetconfBaseOps input) { - return defaultOperation.isPresent() - ? input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure, defaultOperation.get(), + + return perfomRequestWithTimeout(operation, defaultOperation.isPresent() + ? input.editConfigCandidate(new NetconfRpcFutureCallback(operation, id), editStructure, defaultOperation.get(), rollbackSupport) - : input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure, - rollbackSupport); + : input.editConfigCandidate(new NetconfRpcFutureCallback(operation, id), editStructure, + rollbackSupport)); } }); } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java index ffc2b0c65c..7930ed757f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/WriteRunningTx.java @@ -13,6 +13,7 @@ import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; + import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; @@ -59,11 +60,12 @@ public class WriteRunningTx extends AbstractWriteTx { } private void lock() { + final String operation = "Lock running"; try { - invokeBlocking("Lock running", new Function>() { + invokeBlocking(operation, new Function>() { @Override public ListenableFuture apply(final NetconfBaseOps input) { - return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id)); + return perfomRequestWithTimeout(operation, input.lockRunning(new NetconfRpcFutureCallback(operation, id))); } }); } catch (final NetconfDocumentedException e) { @@ -94,14 +96,14 @@ public class WriteRunningTx extends AbstractWriteTx { @Override public synchronized CheckedFuture submit() { - final ListenableFuture commmitFutureAsVoid = Futures.transform(commit(), new Function, Void>() { + final ListenableFuture commitFutureAsVoid = Futures.transform(commit(), new Function, Void>() { @Override public Void apply(final RpcResult input) { return null; } }); - return Futures.makeChecked(commmitFutureAsVoid, new Function() { + return Futures.makeChecked(commitFutureAsVoid, new Function() { @Override public TransactionCommitFailedException apply(final Exception input) { return new TransactionCommitFailedException("Submit of transaction " + getIdentifier() + " failed", input); @@ -117,24 +119,26 @@ public class WriteRunningTx extends AbstractWriteTx { @Override protected void editConfig(final DataContainerChild editStructure, final Optional defaultOperation) throws NetconfDocumentedException { - invokeBlocking("Edit running", new Function>() { + final String operation = "Edit running"; + invokeBlocking(operation, new Function>() { @Override public ListenableFuture apply(final NetconfBaseOps input) { - return defaultOperation.isPresent() - ? input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure, defaultOperation.get(), + return perfomRequestWithTimeout(operation, defaultOperation.isPresent() + ? input.editConfigRunning(new NetconfRpcFutureCallback(operation, id), editStructure, defaultOperation.get(), rollbackSupport) - : input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure, - rollbackSupport); + : input.editConfigRunning(new NetconfRpcFutureCallback(operation, id), editStructure, + rollbackSupport)); } }); } private void unlock() { + final String operation = "Unlocking running"; try { - invokeBlocking("Unlocking running", new Function>() { + invokeBlocking(operation, new Function>() { @Override public ListenableFuture apply(final NetconfBaseOps input) { - return input.unlockRunning(new NetconfRpcFutureCallback("Unlock running", id)); + return perfomRequestWithTimeout(operation, input.unlockRunning(new NetconfRpcFutureCallback(operation, id))); } }); } catch (final NetconfDocumentedException e) { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java index e8587d609d..2a5a0998f1 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java @@ -13,15 +13,27 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.controller.sal.connect.netconf.util.NetconfBaseOps; @@ -31,7 +43,12 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +@PrepareForTest({NetconfBaseOps.class}) +@RunWith(PowerMockRunner.class) public class ReadOnlyTxTest { private static final YangInstanceIdentifier path = YangInstanceIdentifier.create(); @@ -53,11 +70,33 @@ public class ReadOnlyTxTest { public void testRead() throws Exception { final NetconfBaseOps netconfOps = new NetconfBaseOps(rpc, mock(SchemaContext.class)); - final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196))); + final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)), 60000L); readOnlyTx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create()); verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)), any(NormalizedNode.class)); readOnlyTx.read(LogicalDatastoreType.OPERATIONAL, path); verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_QNAME)), any(NormalizedNode.class)); } + + @SuppressWarnings("unchecked") + @Test + public void testReadTimeout() throws Exception { + final ListenableFuture future = mock(ListenableFuture.class); + + Mockito.when(future.get(Mockito.anyLong(), any(TimeUnit.class))).then(new Answer() { + @Override + public DOMRpcResult answer(InvocationOnMock invocation) + throws Throwable { + throw new TimeoutException("Processing Timeout"); + } + }); + + final NetconfBaseOps netconfOps = PowerMockito.mock(NetconfBaseOps.class); + Mockito.when(netconfOps.getConfigRunning(any(FutureCallback.class), any(Optional.class))).thenReturn(future); + + + final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)), 100L); + Assert.assertNull("Read operation didn't correctly timeout", readOnlyTx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create())); + readOnlyTx.close(); + } } \ No newline at end of file