From: Robert Varga Date: Sun, 29 May 2016 19:35:08 +0000 (+0200) Subject: BUG-5280: introduce the notion of client actor time X-Git-Tag: release/boron~109 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=d8bc95c84030468dfe7d04b72499d798dd374331 BUG-5280: introduce the notion of client actor time Time is exposed as a Ticker, with the implementation returning the system ticker. Add a few basic tests and testin infrastructure to control the Ticker's passage of time. Change-Id: I2bb1a8d9555979d38434602d68d48aeef66ae78a Signed-off-by: Robert Varga --- diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContext.java index ca393608b7..9fc17b53fc 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContext.java @@ -10,25 +10,43 @@ package org.opendaylight.controller.cluster.datastore.actors.client; import akka.actor.ActorRef; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; +import com.google.common.base.Ticker; +import javax.annotation.Nonnull; import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; import org.opendaylight.yangtools.concepts.Identifiable; /** - * An actor context associated with this {@link AbstractClientActor} + * An actor context associated with this {@link AbstractClientActor}. + * + * Time-keeping in a client actor is based on monotonic time. The precision of this time can be expected to be the + * same as {@link System#nanoTime()}, but it is not tied to that particular clock. Actor clock is exposed as + * a {@link Ticker}, which can be obtained via {@link #ticker()}. * * @author Robert Varga */ @Beta -public final class ClientActorContext extends AbstractClientActorContext implements Identifiable { +public class ClientActorContext extends AbstractClientActorContext implements Identifiable { private final ClientIdentifier identifier; + // Hidden to avoid subclassing ClientActorContext(final ActorRef self, final String persistenceId, final ClientIdentifier identifier) { super(self, persistenceId); this.identifier = Preconditions.checkNotNull(identifier); } @Override - public ClientIdentifier getIdentifier() { + public @Nonnull ClientIdentifier getIdentifier() { return identifier; } + + /** + * Return the time ticker for this {@link ClientActorContext}. This should be used for in all time-tracking + * done within a client actor. Subclasses of {@link ClientActorBehavior} are encouraged to use + * {@link com.google.common.base.Stopwatch}. + * + * @return Client actor time source + */ + public @Nonnull Ticker ticker() { + return Ticker.systemTicker(); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/MessageTrackerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/MessageTrackerTest.java index 5b7d1a6e46..9c11c13c25 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/MessageTrackerTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/MessageTrackerTest.java @@ -11,7 +11,6 @@ package org.opendaylight.controller.cluster.common.actor; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import com.google.common.base.Ticker; import java.util.List; import org.junit.Assert; import org.junit.Before; @@ -20,20 +19,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MessageTrackerTest { - - private static final class TestTicker extends Ticker { - private long ticks; - - @Override - public long read() { - return ticks; - } - - void increment(final long ticks) { - this.ticks += ticks; - } - } - private static final class Foo { // Intentionally empty } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/TestTicker.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/TestTicker.java new file mode 100644 index 0000000000..3387eca0e6 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/common/actor/TestTicker.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.common.actor; + +import com.google.common.base.Ticker; + +/** + * Utility {@link Ticker} where passage of time is explicitly controlled via {@link #increment(long)}. + * + * @author Robert Varga + */ +public final class TestTicker extends Ticker { + private long counter = 0; + + @Override + public long read() { + return counter; + } + + public long increment(final long ticks) { + counter += ticks; + return counter; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/AbstractClientActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/AbstractClientActorTest.java new file mode 100644 index 0000000000..ee8a2a3bd5 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/AbstractClientActorTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.actors.client; + +import static org.mockito.Mockito.doReturn; +import akka.actor.ActorRef; +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier; +import org.opendaylight.controller.cluster.access.concepts.FrontendType; +import org.opendaylight.controller.cluster.access.concepts.MemberName; +import org.opendaylight.controller.cluster.common.actor.TestTicker; + +/** + * Abstract base class for client actors and their components. + * + * @author Robert Varga + */ +public abstract class AbstractClientActorTest { + private static final MemberName MEMBER_NAME = MemberName.forName("member-1"); + + @Mock + private ClientActorContext mockActorContext; + @Mock + private ActorRef mockSelf; + + protected final TestTicker ticker = new TestTicker(); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + final FrontendType frontendType = FrontendType.forName(getClass().getSimpleName()); + final FrontendIdentifier frontendId = FrontendIdentifier.create(MEMBER_NAME, frontendType); + final ClientIdentifier clientId = ClientIdentifier.create(frontendId, 0); + + doReturn(ticker).when(mockActorContext).ticker(); + doReturn(clientId).when(mockActorContext).getIdentifier(); + doReturn(getClass().getSimpleName()).when(mockActorContext).persistenceId(); + doReturn(mockSelf).when(mockActorContext).self(); + } + + protected final ClientActorContext actorContext() { + return mockActorContext; + } + + protected final ActorRef self() { + return mockSelf; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContextTest.java new file mode 100644 index 0000000000..f575815cb1 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorContextTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.cluster.datastore.actors.client; + +import static org.junit.Assert.assertSame; +import akka.actor.ActorRef; +import com.google.common.base.Ticker; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; +import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier; +import org.opendaylight.controller.cluster.access.concepts.FrontendType; +import org.opendaylight.controller.cluster.access.concepts.MemberName; +import org.opendaylight.controller.cluster.datastore.ShardTransactionTest; + +public class ClientActorContextTest { + private static final MemberName MEMBER_NAME = MemberName.forName("member-1"); + private static final FrontendType FRONTEND_TYPE = FrontendType.forName(ShardTransactionTest.class.getSimpleName()); + private static final FrontendIdentifier FRONTEND_ID = FrontendIdentifier.create(MEMBER_NAME, FRONTEND_TYPE); + private static final ClientIdentifier CLIENT_ID = ClientIdentifier.create(FRONTEND_ID, 0); + private static final String PERSISTENCE_ID = ClientActorContextTest.class.getSimpleName(); + + @Mock + private ActorRef mockSelf; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testMockingControl() { + ClientActorContext ctx = new ClientActorContext(mockSelf, PERSISTENCE_ID, CLIENT_ID); + assertSame(CLIENT_ID, ctx.getIdentifier()); + assertSame(PERSISTENCE_ID, ctx.persistenceId()); + assertSame(mockSelf, ctx.self()); + } + + @Test + public void testTicker() { + ClientActorContext ctx = new ClientActorContext(mockSelf, PERSISTENCE_ID, CLIENT_ID); + assertSame(Ticker.systemTicker(), ctx.ticker()); + } +}