[ONOS-6594] Upgrade to Atomix 2.0.0
Change-Id: I6534bca1c8570b4e017f682953b876da29146675
diff --git a/core/store/primitives/BUCK b/core/store/primitives/BUCK
index 0758c3e..e90ea5d 100644
--- a/core/store/primitives/BUCK
+++ b/core/store/primitives/BUCK
@@ -9,13 +9,11 @@
TEST_DEPS = [
'//lib:TEST',
'//core/api:onos-api-tests',
- '//lib:netty-transport',
- '//lib:catalyst-transport',
'//lib:netty-handler',
'//lib:netty-buffer',
'//lib:netty-codec',
'//lib:netty-resolver',
-
+ '//lib:commons-math3',
]
osgi_jar_with_tests (
diff --git a/core/store/primitives/pom.xml b/core/store/primitives/pom.xml
index c46a7ca..ee89881 100644
--- a/core/store/primitives/pom.xml
+++ b/core/store/primitives/pom.xml
@@ -70,20 +70,10 @@
<dependency>
<groupId>io.atomix</groupId>
<artifactId>atomix</artifactId>
- <version>1.0.8</version>
+ <version>2.0.0-alpha1</version>
</dependency>
<dependency>
- <groupId>io.atomix.catalyst</groupId>
- <artifactId>catalyst-netty</artifactId>
- <version>1.2.1</version>
- </dependency>
- <dependency>
- <groupId>io.atomix.catalyst</groupId>
- <artifactId>catalyst-transport</artifactId>
- <version>1.2.1</version>
- </dependency>
- <dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-junit</artifactId>
<scope>test</scope>
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java
deleted file mode 100644
index 2c4ee73..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.TypeSerializerFactory;
-import io.atomix.manager.util.ResourceManagerTypeResolver;
-import io.atomix.variables.internal.LongCommands;
-import org.onlab.util.Match;
-import org.onosproject.cluster.Leader;
-import org.onosproject.cluster.Leadership;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.event.Change;
-import org.onosproject.store.primitives.MapUpdate;
-import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapFactory;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapFactory;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapFactory;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeFactory;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorFactory;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueFactory;
-import org.onosproject.store.primitives.resources.impl.CommitResult;
-import org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult;
-import org.onosproject.store.primitives.resources.impl.MapEntryUpdateResult;
-import org.onosproject.store.primitives.resources.impl.PrepareResult;
-import org.onosproject.store.primitives.resources.impl.RollbackResult;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.DocumentPath;
-import org.onosproject.store.service.DocumentTreeEvent;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.TransactionLog;
-import org.onosproject.store.service.MultimapEvent;
-import org.onosproject.store.service.Task;
-import org.onosproject.store.service.Versioned;
-import org.onosproject.store.service.WorkQueueStats;
-
-import java.util.Arrays;
-import java.util.Optional;
-
-/**
- * Serializer utility for Atomix Catalyst.
- */
-public final class CatalystSerializers {
-
- private CatalystSerializers() {
- }
-
- public static Serializer getSerializer() {
- Serializer serializer = new Serializer();
- TypeSerializerFactory factory =
- new DefaultCatalystTypeSerializerFactory(
- org.onosproject.store.service.Serializer.using(Arrays.asList((KryoNamespaces.API)),
- MapEntryUpdateResult.class,
- MapEntryUpdateResult.Status.class,
- Transaction.State.class,
- PrepareResult.class,
- CommitResult.class,
- DocumentPath.class,
- DocumentTreeUpdateResult.class,
- DocumentTreeUpdateResult.Status.class,
- DocumentTreeEvent.class,
- DocumentTreeEvent.Type.class,
- RollbackResult.class));
- // ONOS classes
- serializer.register(Change.class, factory);
- serializer.register(Leader.class, factory);
- serializer.register(Leadership.class, factory);
- serializer.register(NodeId.class, factory);
- serializer.register(Match.class, factory);
- serializer.register(MapEntryUpdateResult.class, factory);
- serializer.register(MapEntryUpdateResult.Status.class, factory);
- serializer.register(Transaction.State.class, factory);
- serializer.register(PrepareResult.class, factory);
- serializer.register(CommitResult.class, factory);
- serializer.register(RollbackResult.class, factory);
- serializer.register(TransactionId.class, factory);
- serializer.register(MapUpdate.class, factory);
- serializer.register(MapUpdate.Type.class, factory);
- serializer.register(TransactionLog.class, factory);
- serializer.register(Versioned.class, factory);
- serializer.register(MapEvent.class, factory);
- serializer.register(MultimapEvent.class, factory);
- serializer.register(MultimapEvent.Type.class, factory);
- serializer.register(Task.class, factory);
- serializer.register(WorkQueueStats.class, factory);
- serializer.register(DocumentPath.class, factory);
- serializer.register(DocumentTreeUpdateResult.class, factory);
- serializer.register(DocumentTreeUpdateResult.Status.class, factory);
- serializer.register(DocumentTreeEvent.class, factory);
- serializer.register(Maps.immutableEntry("a", "b").getClass(), factory);
- serializer.register(ImmutableList.of().getClass(), factory);
- serializer.register(ImmutableList.of("a").getClass(), factory);
- serializer.register(Arrays.asList().getClass(), factory);
- serializer.register(HashMultiset.class, factory);
- serializer.register(Optional.class, factory);
-
- serializer.resolve(new LongCommands.TypeResolver());
- serializer.resolve(new AtomixConsistentMapCommands.TypeResolver());
- serializer.resolve(new AtomixLeaderElectorCommands.TypeResolver());
- serializer.resolve(new AtomixWorkQueueCommands.TypeResolver());
- serializer.resolve(new AtomixDocumentTreeCommands.TypeResolver());
- serializer.resolve(new ResourceManagerTypeResolver());
- serializer.resolve(new AtomixConsistentTreeMapCommands.TypeResolver());
- serializer.resolve(new AtomixConsistentMultimapCommands.TypeResolver());
-
- serializer.registerClassLoader(AtomixConsistentMapFactory.class)
- .registerClassLoader(AtomixLeaderElectorFactory.class)
- .registerClassLoader(AtomixWorkQueueFactory.class)
- .registerClassLoader(AtomixDocumentTreeFactory.class)
- .registerClassLoader(AtomixConsistentTreeMapFactory.class)
- .registerClassLoader(AtomixConsistentSetMultimapFactory.class);
-
- return serializer;
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java
deleted file mode 100644
index fc94dd6..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Maps;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.Client;
-import io.atomix.catalyst.transport.Server;
-import io.atomix.catalyst.transport.Transport;
-import org.onlab.packet.IpAddress;
-import org.onosproject.cluster.PartitionId;
-import org.onosproject.store.cluster.messaging.Endpoint;
-import org.onosproject.store.cluster.messaging.MessagingService;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Map;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Copycat transport implementation built on {@link MessagingService}.
- */
-public class CopycatTransport implements Transport {
- private final PartitionId partitionId;
- private final MessagingService messagingService;
- private static final Map<Address, Endpoint> EP_LOOKUP_CACHE = Maps.newConcurrentMap();
-
- static final byte MESSAGE = 0x01;
- static final byte CONNECT = 0x02;
- static final byte CLOSE = 0x03;
-
- static final byte SUCCESS = 0x01;
- static final byte FAILURE = 0x02;
-
- public CopycatTransport(PartitionId partitionId, MessagingService messagingService) {
- this.partitionId = checkNotNull(partitionId, "partitionId cannot be null");
- this.messagingService = checkNotNull(messagingService, "messagingService cannot be null");
- }
-
- @Override
- public Client client() {
- return new CopycatTransportClient(partitionId, messagingService);
- }
-
- @Override
- public Server server() {
- return new CopycatTransportServer(partitionId, messagingService);
- }
-
- @Override
- public String toString() {
- return toStringHelper(this).toString();
- }
-
- /**
- * Maps {@link Address address} to {@link Endpoint endpoint}.
- * @param address address
- * @return end point
- */
- static Endpoint toEndpoint(Address address) {
- return EP_LOOKUP_CACHE.computeIfAbsent(address, a -> {
- try {
- return new Endpoint(IpAddress.valueOf(InetAddress.getByName(a.host())), a.port());
- } catch (UnknownHostException e) {
- Throwables.propagate(e);
- return null;
- }
- });
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java
deleted file mode 100644
index afa98cc..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Sets;
-import io.atomix.catalyst.concurrent.ThreadContext;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.Client;
-import io.atomix.catalyst.transport.Connection;
-import io.atomix.catalyst.transport.TransportException;
-import org.onosproject.cluster.PartitionId;
-import org.onosproject.store.cluster.messaging.Endpoint;
-import org.onosproject.store.cluster.messaging.MessagingException;
-import org.onosproject.store.cluster.messaging.MessagingService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.ConnectException;
-import java.nio.ByteBuffer;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.store.primitives.impl.CopycatTransport.CONNECT;
-import static org.onosproject.store.primitives.impl.CopycatTransport.SUCCESS;
-
-/**
- * Copycat transport client implementation.
- */
-public class CopycatTransportClient implements Client {
- private final Logger log = LoggerFactory.getLogger(getClass());
- private final PartitionId partitionId;
- private final String serverSubject;
- private final MessagingService messagingService;
- private final Set<CopycatTransportConnection> connections = Sets.newConcurrentHashSet();
-
- public CopycatTransportClient(PartitionId partitionId, MessagingService messagingService) {
- this.partitionId = checkNotNull(partitionId, "partitionId cannot be null");
- this.serverSubject = String.format("onos-copycat-%s", partitionId);
- this.messagingService = checkNotNull(messagingService, "messagingService cannot be null");
- }
-
- @Override
- public CompletableFuture<Connection> connect(Address address) {
- CompletableFuture<Connection> future = new CompletableFuture<>();
- ThreadContext context = ThreadContext.currentContextOrThrow();
- Endpoint endpoint = CopycatTransport.toEndpoint(address);
-
- log.debug("Connecting to {}", address);
-
- ByteBuffer requestBuffer = ByteBuffer.allocate(1);
- requestBuffer.put(CONNECT);
-
- // Send a connect request to the server to get a unique connection ID.
- messagingService.sendAndReceive(endpoint, serverSubject, requestBuffer.array(), context.executor())
- .whenComplete((payload, error) -> {
- Throwable wrappedError = error;
- if (error != null) {
- Throwable rootCause = Throwables.getRootCause(error);
- if (MessagingException.class.isAssignableFrom(rootCause.getClass())) {
- wrappedError = new TransportException(error);
- }
- // TODO ONOS-6788 we might consider demoting this warning during startup when there is
- // a race between the server registering handlers and the client sending messages
- log.warn("Connection to {} failed! Reason: {}", address, wrappedError);
- future.completeExceptionally(wrappedError);
- } else {
- // If the connection is successful, the server will send back a
- // connection ID indicating where to send messages for the connection.
- ByteBuffer responseBuffer = ByteBuffer.wrap(payload);
- if (responseBuffer.get() == SUCCESS) {
- long connectionId = responseBuffer.getLong();
- CopycatTransportConnection connection = new CopycatTransportConnection(
- connectionId,
- CopycatTransportConnection.Mode.CLIENT,
- partitionId,
- endpoint,
- messagingService,
- context);
- connection.onClose(connections::remove);
- connections.add(connection);
- future.complete(connection);
- log.debug("Created connection {}-{} to {}", partitionId, connectionId, address);
- } else {
- log.warn("Connection to {} failed!");
- future.completeExceptionally(new ConnectException());
- }
- }
- });
- return future;
- }
-
- @Override
- public CompletableFuture<Void> close() {
- return CompletableFuture.allOf(connections.stream().map(Connection::close).toArray(CompletableFuture[]::new));
- }
-
- @Override
- public String toString() {
- return toStringHelper(this)
- .add("partitionId", partitionId)
- .toString();
- }
-}
-
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java
deleted file mode 100644
index a3a8539..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import com.google.common.base.Throwables;
-import io.atomix.catalyst.concurrent.Listener;
-import io.atomix.catalyst.concurrent.Listeners;
-import io.atomix.catalyst.concurrent.ThreadContext;
-import io.atomix.catalyst.serializer.SerializationException;
-import io.atomix.catalyst.transport.Connection;
-import io.atomix.catalyst.transport.TransportException;
-import io.atomix.catalyst.util.reference.ReferenceCounted;
-import org.apache.commons.io.IOUtils;
-import org.onlab.util.Tools;
-import org.onosproject.cluster.PartitionId;
-import org.onosproject.store.cluster.messaging.Endpoint;
-import org.onosproject.store.cluster.messaging.MessagingException;
-import org.onosproject.store.cluster.messaging.MessagingService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.store.primitives.impl.CopycatTransport.CLOSE;
-import static org.onosproject.store.primitives.impl.CopycatTransport.FAILURE;
-import static org.onosproject.store.primitives.impl.CopycatTransport.MESSAGE;
-import static org.onosproject.store.primitives.impl.CopycatTransport.SUCCESS;
-
-/**
- * Base Copycat Transport connection.
- */
-public class CopycatTransportConnection implements Connection {
- private static final int MAX_MESSAGE_SIZE = 1024 * 1024;
-
- private final Logger log = LoggerFactory.getLogger(getClass());
- private final long connectionId;
- private final String localSubject;
- private final String remoteSubject;
- private final PartitionId partitionId;
- private final Endpoint endpoint;
- private final MessagingService messagingService;
- private final ThreadContext context;
- private final Map<Class, InternalHandler> handlers = new ConcurrentHashMap<>();
- private final Listeners<Throwable> exceptionListeners = new Listeners<>();
- private final Listeners<Connection> closeListeners = new Listeners<>();
-
- CopycatTransportConnection(
- long connectionId,
- Mode mode,
- PartitionId partitionId,
- Endpoint endpoint,
- MessagingService messagingService,
- ThreadContext context) {
- this.connectionId = connectionId;
- this.partitionId = checkNotNull(partitionId, "partitionId cannot be null");
- this.localSubject = mode.getLocalSubject(partitionId, connectionId);
- this.remoteSubject = mode.getRemoteSubject(partitionId, connectionId);
- this.endpoint = checkNotNull(endpoint, "endpoint cannot be null");
- this.messagingService = checkNotNull(messagingService, "messagingService cannot be null");
- this.context = checkNotNull(context, "context cannot be null");
- messagingService.registerHandler(localSubject, this::handle);
- }
-
- @Override
- public CompletableFuture<Void> send(Object message) {
- ThreadContext context = ThreadContext.currentContextOrThrow();
- CompletableFuture<Void> future = new CompletableFuture<>();
- try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
- DataOutputStream dos = new DataOutputStream(baos);
- dos.writeByte(MESSAGE);
- context.serializer().writeObject(message, baos);
- if (message instanceof ReferenceCounted) {
- ((ReferenceCounted<?>) message).release();
- }
-
- byte[] bytes = baos.toByteArray();
- if (bytes.length > MAX_MESSAGE_SIZE) {
- throw new IllegalArgumentException(message + " exceeds maximum message size " + MAX_MESSAGE_SIZE);
- }
- messagingService.sendAsync(endpoint, remoteSubject, bytes)
- .whenComplete((r, e) -> {
- if (e != null) {
- context.executor().execute(() -> future.completeExceptionally(e));
- } else {
- context.executor().execute(() -> future.complete(null));
- }
- });
- } catch (SerializationException | IOException e) {
- future.completeExceptionally(e);
- }
- return future;
- }
-
- @Override
- public <T, U> CompletableFuture<U> sendAndReceive(T message) {
- ThreadContext context = ThreadContext.currentContextOrThrow();
- CompletableFuture<U> future = new CompletableFuture<>();
- try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
- DataOutputStream dos = new DataOutputStream(baos);
- dos.writeByte(MESSAGE);
- context.serializer().writeObject(message, baos);
- if (message instanceof ReferenceCounted) {
- ((ReferenceCounted<?>) message).release();
- }
-
- byte[] bytes = baos.toByteArray();
- if (bytes.length > MAX_MESSAGE_SIZE) {
- throw new IllegalArgumentException(message + " exceeds maximum message size " + MAX_MESSAGE_SIZE);
- }
- messagingService.sendAndReceive(endpoint,
- remoteSubject,
- bytes,
- context.executor())
- .whenComplete((response, error) -> handleResponse(response, error, future));
- } catch (SerializationException | IOException e) {
- future.completeExceptionally(e);
- }
- return future;
- }
-
- /**
- * Handles a response received from the other side of the connection.
- */
- private <T> void handleResponse(
- byte[] response,
- Throwable error,
- CompletableFuture<T> future) {
- if (error != null) {
- Throwable rootCause = Throwables.getRootCause(error);
- if (rootCause instanceof MessagingException.NoRemoteHandler) {
- future.completeExceptionally(new TransportException(error));
- close(rootCause);
- } else if (rootCause instanceof SocketException) {
- future.completeExceptionally(new TransportException(error));
- } else {
- future.completeExceptionally(error);
- }
- return;
- }
-
- checkNotNull(response);
- InputStream input = new ByteArrayInputStream(response);
- try {
- byte status = (byte) input.read();
- if (status == FAILURE) {
- Throwable t = context.serializer().readObject(input);
- future.completeExceptionally(t);
- } else {
- try {
- future.complete(context.serializer().readObject(input));
- } catch (SerializationException e) {
- future.completeExceptionally(e);
- }
- }
- } catch (IOException e) {
- future.completeExceptionally(e);
- }
- }
-
- /**
- * Handles a message sent to the connection.
- */
- private CompletableFuture<byte[]> handle(Endpoint sender, byte[] payload) {
- try (DataInputStream input = new DataInputStream(new ByteArrayInputStream(payload))) {
- byte type = input.readByte();
- switch (type) {
- case MESSAGE:
- return handleMessage(IOUtils.toByteArray(input));
- case CLOSE:
- return handleClose();
- default:
- throw new IllegalStateException("Invalid message type");
- }
- } catch (IOException e) {
- Throwables.propagate(e);
- return null;
- }
- }
-
- /**
- * Handles a message from the other side of the connection.
- */
- @SuppressWarnings("unchecked")
- private CompletableFuture<byte[]> handleMessage(byte[] message) {
- try {
- Object request = context.serializer().readObject(new ByteArrayInputStream(message));
- InternalHandler handler = handlers.get(request.getClass());
- if (handler == null) {
- log.warn("No handler registered on connection {}-{} for type {}",
- partitionId, connectionId, request.getClass());
- return Tools.exceptionalFuture(new IllegalStateException(
- "No handler registered for " + request.getClass()));
- }
-
- return handler.handle(request).handle((result, error) -> {
- try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
- baos.write(error != null ? FAILURE : SUCCESS);
- context.serializer().writeObject(error != null ? error : result, baos);
- byte[] bytes = baos.toByteArray();
- if (bytes.length > MAX_MESSAGE_SIZE) {
- throw new IllegalArgumentException("response exceeds maximum message size " + MAX_MESSAGE_SIZE);
- }
- return bytes;
- } catch (IOException e) {
- Throwables.propagate(e);
- return null;
- }
- });
- } catch (Exception e) {
- return Tools.exceptionalFuture(e);
- }
- }
-
- /**
- * Handles a close request from the other side of the connection.
- */
- private CompletableFuture<byte[]> handleClose() {
- CompletableFuture<byte[]> future = new CompletableFuture<>();
- context.executor().execute(() -> {
- close(null);
- ByteBuffer responseBuffer = ByteBuffer.allocate(1);
- responseBuffer.put(SUCCESS);
- future.complete(responseBuffer.array());
- });
- return future;
- }
-
- @Override
- public <T, U> Connection handler(Class<T> type, Consumer<T> handler) {
- return handler(type, r -> {
- handler.accept(r);
- return null;
- });
- }
-
- @Override
- public <T, U> Connection handler(Class<T> type, Function<T, CompletableFuture<U>> handler) {
- if (log.isTraceEnabled()) {
- log.trace("Registered handler on connection {}-{}: {}", partitionId, connectionId, type);
- }
- handlers.put(type, new InternalHandler(handler, ThreadContext.currentContextOrThrow()));
- return this;
- }
-
- @Override
- public Listener<Throwable> onException(Consumer<Throwable> consumer) {
- return exceptionListeners.add(consumer);
- }
-
- @Override
- public Listener<Connection> onClose(Consumer<Connection> consumer) {
- return closeListeners.add(consumer);
- }
-
- @Override
- public CompletableFuture<Void> close() {
- log.debug("Closing connection {}-{}", partitionId, connectionId);
-
- ByteBuffer requestBuffer = ByteBuffer.allocate(1);
- requestBuffer.put(CLOSE);
-
- ThreadContext context = ThreadContext.currentContextOrThrow();
- CompletableFuture<Void> future = new CompletableFuture<>();
- messagingService.sendAndReceive(endpoint, remoteSubject, requestBuffer.array(), context.executor())
- .whenComplete((payload, error) -> {
- close(error);
- Throwable wrappedError = error;
- if (error != null) {
- Throwable rootCause = Throwables.getRootCause(error);
- if (rootCause instanceof MessagingException.NoRemoteHandler) {
- wrappedError = new TransportException(error);
- }
- future.completeExceptionally(wrappedError);
- } else {
- ByteBuffer responseBuffer = ByteBuffer.wrap(payload);
- if (responseBuffer.get() == SUCCESS) {
- future.complete(null);
- } else {
- future.completeExceptionally(new TransportException("Failed to close connection"));
- }
- }
- });
- return future;
- }
-
- /**
- * Cleans up the connection, unregistering handlers registered on the MessagingService.
- */
- private void close(Throwable error) {
- log.debug("Connection {}-{} closed", partitionId, connectionId);
- messagingService.unregisterHandler(localSubject);
- if (error != null) {
- exceptionListeners.accept(error);
- }
- closeListeners.accept(this);
- }
-
- /**
- * Connection mode used to indicate whether this side of the connection is
- * a client or server.
- */
- enum Mode {
-
- /**
- * Represents the client side of a bi-directional connection.
- */
- CLIENT {
- @Override
- String getLocalSubject(PartitionId partitionId, long connectionId) {
- return String.format("onos-copycat-%s-%d-client", partitionId, connectionId);
- }
-
- @Override
- String getRemoteSubject(PartitionId partitionId, long connectionId) {
- return String.format("onos-copycat-%s-%d-server", partitionId, connectionId);
- }
- },
-
- /**
- * Represents the server side of a bi-directional connection.
- */
- SERVER {
- @Override
- String getLocalSubject(PartitionId partitionId, long connectionId) {
- return String.format("onos-copycat-%s-%d-server", partitionId, connectionId);
- }
-
- @Override
- String getRemoteSubject(PartitionId partitionId, long connectionId) {
- return String.format("onos-copycat-%s-%d-client", partitionId, connectionId);
- }
- };
-
- /**
- * Returns the local messaging service subject for the connection in this mode.
- * Subjects generated by the connection mode are guaranteed to be globally unique.
- *
- * @param partitionId the partition ID to which the connection belongs.
- * @param connectionId the connection ID.
- * @return the globally unique local subject for the connection.
- */
- abstract String getLocalSubject(PartitionId partitionId, long connectionId);
-
- /**
- * Returns the remote messaging service subject for the connection in this mode.
- * Subjects generated by the connection mode are guaranteed to be globally unique.
- *
- * @param partitionId the partition ID to which the connection belongs.
- * @param connectionId the connection ID.
- * @return the globally unique remote subject for the connection.
- */
- abstract String getRemoteSubject(PartitionId partitionId, long connectionId);
- }
-
- /**
- * Internal container for a handler/context pair.
- */
- private static class InternalHandler {
- private final Function handler;
- private final ThreadContext context;
-
- InternalHandler(Function handler, ThreadContext context) {
- this.handler = handler;
- this.context = context;
- }
-
- @SuppressWarnings("unchecked")
- CompletableFuture<Object> handle(Object message) {
- CompletableFuture<Object> future = new CompletableFuture<>();
- context.executor().execute(() -> {
- CompletableFuture<Object> responseFuture = (CompletableFuture<Object>) handler.apply(message);
- if (responseFuture != null) {
- responseFuture.whenComplete((r, e) -> {
- if (e != null) {
- future.completeExceptionally((Throwable) e);
- } else {
- future.complete(r);
- }
- });
- }
- });
- return future;
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java
deleted file mode 100644
index 8de05a3..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import com.google.common.collect.Sets;
-import io.atomix.catalyst.concurrent.ThreadContext;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.Connection;
-import io.atomix.catalyst.transport.Server;
-import org.apache.commons.lang3.RandomUtils;
-import org.onosproject.cluster.PartitionId;
-import org.onosproject.store.cluster.messaging.MessagingService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.nio.ByteBuffer;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.store.primitives.impl.CopycatTransport.CONNECT;
-import static org.onosproject.store.primitives.impl.CopycatTransport.FAILURE;
-import static org.onosproject.store.primitives.impl.CopycatTransport.SUCCESS;
-
-/**
- * Copycat transport server implementation.
- */
-public class CopycatTransportServer implements Server {
- private final Logger log = LoggerFactory.getLogger(getClass());
- private final PartitionId partitionId;
- private final String serverSubject;
- private final MessagingService messagingService;
- private final Set<CopycatTransportConnection> connections = Sets.newConcurrentHashSet();
-
- public CopycatTransportServer(PartitionId partitionId, MessagingService messagingService) {
- this.partitionId = checkNotNull(partitionId, "partitionId cannot be null");
- this.serverSubject = String.format("onos-copycat-%s", partitionId);
- this.messagingService = checkNotNull(messagingService, "messagingService cannot be null");
- }
-
- @Override
- public CompletableFuture<Void> listen(Address address, Consumer<Connection> consumer) {
- ThreadContext context = ThreadContext.currentContextOrThrow();
- messagingService.registerHandler(serverSubject, (sender, payload) -> {
-
- // Only connect messages can be sent to the server. Once a connect message
- // is received, the connection will register a separate handler for messaging.
- ByteBuffer requestBuffer = ByteBuffer.wrap(payload);
- if (requestBuffer.get() != CONNECT) {
- ByteBuffer responseBuffer = ByteBuffer.allocate(1);
- responseBuffer.put(FAILURE);
- return CompletableFuture.completedFuture(responseBuffer.array());
- }
-
- // Create the connection and ensure state is cleaned up when the connection is closed.
- long connectionId = RandomUtils.nextLong();
- CopycatTransportConnection connection = new CopycatTransportConnection(
- connectionId,
- CopycatTransportConnection.Mode.SERVER,
- partitionId,
- sender,
- messagingService,
- context);
- connection.onClose(connections::remove);
- connections.add(connection);
-
- CompletableFuture<byte[]> future = new CompletableFuture<>();
-
- // We need to ensure the connection event is called on the Copycat thread
- // and that the future is not completed until the Copycat server has been
- // able to register message handlers, otherwise some messages can be received
- // prior to any handlers being registered.
- context.executor().execute(() -> {
- log.debug("Created connection {}-{}", partitionId, connectionId);
- consumer.accept(connection);
-
- ByteBuffer responseBuffer = ByteBuffer.allocate(9);
- responseBuffer.put(SUCCESS);
- responseBuffer.putLong(connectionId);
- future.complete(responseBuffer.array());
- });
- return future;
- });
- return CompletableFuture.completedFuture(null);
- }
-
- @Override
- public CompletableFuture<Void> close() {
- return CompletableFuture.allOf(connections.stream().map(Connection::close).toArray(CompletableFuture[]::new));
- }
-
- @Override
- public String toString() {
- return toStringHelper(this)
- .add("partitionId", partitionId)
- .toString();
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java
index a189eb2..45cf193 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java
@@ -32,6 +32,6 @@
@Override
public AsyncAtomicCounter build() {
- return primitiveCreator.newAsyncCounter(name(), executorSupplier());
+ return primitiveCreator.newAsyncCounter(name());
}
}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java
index c92ef22..309220a 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java
@@ -33,7 +33,7 @@
@Override
public AsyncAtomicCounterMap<K> buildAsyncMap() {
- return primitiveCreator.newAsyncAtomicCounterMap(name(), serializer(), executorSupplier());
+ return primitiveCreator.newAsyncAtomicCounterMap(name(), serializer());
}
@Override
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java
index ac294f4..c6742cb 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java
@@ -32,6 +32,6 @@
@Override
public AsyncAtomicIdGenerator build() {
- return primitiveCreator.newAsyncIdGenerator(name(), executorSupplier());
+ return primitiveCreator.newAsyncIdGenerator(name());
}
}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java
index a63fe4be..b17983c 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java
@@ -15,7 +15,6 @@
*/
package org.onosproject.store.primitives.impl;
-import java.util.concurrent.Executor;
import java.util.function.Supplier;
import org.onosproject.store.service.AsyncAtomicValue;
@@ -38,12 +37,6 @@
}
@Override
- public AtomicValueBuilder<V> withExecutorSupplier(Supplier<Executor> executorSupplier) {
- mapBuilder.withExecutorSupplier(executorSupplier);
- return this;
- }
-
- @Override
public AsyncAtomicValue<V> build() {
return new DefaultAsyncAtomicValue<>(checkNotNull(name()),
checkNotNull(serializer()),
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java
deleted file mode 100644
index b0d6841..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-import org.onosproject.store.service.Serializer;
-import org.slf4j.Logger;
-
-import com.google.common.base.Throwables;
-
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.TypeSerializer;
-import io.atomix.catalyst.serializer.TypeSerializerFactory;
-
-/**
- * {@link TypeSerializerFactory} for providing {@link TypeSerializer}s based on
- * {@code org.onosproject.store.service.Serializer}.
- */
-public class DefaultCatalystTypeSerializerFactory implements TypeSerializerFactory {
-
- private final Logger log = getLogger(getClass());
- private final TypeSerializer<?> typeSerializer;
-
- public DefaultCatalystTypeSerializerFactory(Serializer serializer) {
- typeSerializer = new InternalSerializer<>(serializer);
- }
-
- @Override
- public TypeSerializer<?> createSerializer(Class<?> clazz) {
- return typeSerializer;
- }
-
- private class InternalSerializer<T> implements TypeSerializer<T> {
-
- private final Serializer serializer;
-
- InternalSerializer(Serializer serializer) {
- this.serializer = serializer;
- }
-
- @Override
- public void write(T object, BufferOutput buffer, io.atomix.catalyst.serializer.Serializer serializer) {
- try {
- byte[] payload = this.serializer.encode(object);
- buffer.writeInt(payload.length);
- buffer.write(payload);
- } catch (Exception e) {
- log.warn("Failed to serialize {}", object, e);
- throw Throwables.propagate(e);
- }
- }
-
- @Override
- public T read(Class<T> type, BufferInput buffer, io.atomix.catalyst.serializer.Serializer serializer) {
- int size = buffer.readInt();
- try {
- byte[] payload = new byte[size];
- buffer.read(payload);
- return this.serializer.decode(payload);
- } catch (Exception e) {
- log.warn("Failed to deserialize as type {}. Payload size: {}", type, size, e);
- throw Throwables.propagate(e);
- }
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java
index 820174d..b5284de 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java
@@ -41,7 +41,7 @@
@Override
public AsyncConsistentMap<K, V> buildAsyncMap() {
- AsyncConsistentMap<K, V> map = primitiveCreator.newAsyncConsistentMap(name(), serializer(), executorSupplier());
+ AsyncConsistentMap<K, V> map = primitiveCreator.newAsyncConsistentMap(name(), serializer());
map = relaxedReadConsistency() ? DistributedPrimitives.newCachingMap(map) : map;
map = readOnly() ? DistributedPrimitives.newUnmodifiableMap(map) : map;
return meteringEnabled() ? DistributedPrimitives.newMeteredMap(map) : map;
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java
index ba7d673..4b23253 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java
@@ -36,7 +36,7 @@
@Override
public AsyncConsistentMultimap<K, V> buildMultimap() {
- return primitiveCreator.newAsyncConsistentSetMultimap(name(), serializer(), executorSupplier());
+ return primitiveCreator.newAsyncConsistentSetMultimap(name(), serializer());
}
@Override
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java
index 5e2a8b4..2aa906a 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java
@@ -35,7 +35,7 @@
@Override
public AsyncConsistentTreeMap<V> buildTreeMap() {
- return primitiveCreator.newAsyncConsistentTreeMap(name(), serializer(), executorSupplier());
+ return primitiveCreator.newAsyncConsistentTreeMap(name(), serializer());
}
@Override
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java
index 5e95180..c17f91d 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java
@@ -15,7 +15,6 @@
*/
package org.onosproject.store.primitives.impl;
-import java.util.concurrent.Executor;
import java.util.function.Supplier;
import org.onosproject.core.ApplicationId;
@@ -54,12 +53,6 @@
}
@Override
- public DistributedSetBuilder<E> withExecutorSupplier(Supplier<Executor> executorSupplier) {
- mapBuilder.withExecutorSupplier(executorSupplier);
- return this;
- }
-
- @Override
public DistributedSetBuilder<E> withPurgeOnUninstall() {
mapBuilder.withPurgeOnUninstall();
return this;
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java
index 65c0504..8d21e60 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java
@@ -47,6 +47,6 @@
@Deprecated
@Override
public AsyncDocumentTree<V> build() {
- return primitiveCreator.newAsyncDocumentTree(name(), serializer(), executorSupplier());
+ return primitiveCreator.newAsyncDocumentTree(name(), serializer());
}
}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java
index 6f8f55d..69788f9 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java
@@ -32,6 +32,6 @@
@Override
public AsyncLeaderElector build() {
- return primitiveCreator.newAsyncLeaderElector(name(), executorSupplier());
+ return primitiveCreator.newAsyncLeaderElector(name());
}
}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java
deleted file mode 100644
index 8955a6d..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import io.atomix.catalyst.concurrent.Listener;
-import io.atomix.catalyst.concurrent.ThreadContext;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.Transport;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.copycat.session.Session;
-
-import java.util.Collection;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-/**
- * {@code CopycatClient} that merely delegates control to
- * another CopycatClient.
- */
-public class DelegatingCopycatClient implements CopycatClient {
-
- protected final CopycatClient client;
-
- DelegatingCopycatClient(CopycatClient client) {
- this.client = client;
- }
-
- @Override
- public State state() {
- return client.state();
- }
-
- @Override
- public Listener<State> onStateChange(Consumer<State> callback) {
- return client.onStateChange(callback);
- }
-
- @Override
- public ThreadContext context() {
- return client.context();
- }
-
- @Override
- public Transport transport() {
- return client.transport();
- }
-
- @Override
- public Serializer serializer() {
- return client.serializer();
- }
-
- @Override
- public Session session() {
- return client.session();
- }
-
- @Override
- public <T> CompletableFuture<T> submit(Command<T> command) {
- return client.submit(command);
- }
-
- @Override
- public <T> CompletableFuture<T> submit(Query<T> query) {
- return client.submit(query);
- }
-
- @Override
- public Listener<Void> onEvent(String event, Runnable callback) {
- return client.onEvent(event, callback);
- }
-
- @Override
- public <T> Listener<T> onEvent(String event, Consumer<T> callback) {
- return client.onEvent(event, callback);
- }
-
- @Override
- public CompletableFuture<CopycatClient> connect(Collection<Address> members) {
- return client.connect(members);
- }
-
- @Override
- public CompletableFuture<CopycatClient> recover() {
- return client.recover();
- }
-
- @Override
- public CompletableFuture<Void> close() {
- return client.close();
- }
-}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java
deleted file mode 100644
index 504fa75..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-import org.onosproject.store.service.AsyncAtomicCounter;
-
-/**
- * {@link AsyncAtomicCounter} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingAsyncAtomicCounter extends ExecutingDistributedPrimitive implements AsyncAtomicCounter {
- private final AsyncAtomicCounter delegateCounter;
-
- public ExecutingAsyncAtomicCounter(
- AsyncAtomicCounter delegateCounter, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateCounter, orderedExecutor, threadPoolExecutor);
- this.delegateCounter = delegateCounter;
- }
-
- @Override
- public CompletableFuture<Long> incrementAndGet() {
- return asyncFuture(delegateCounter.incrementAndGet());
- }
-
- @Override
- public CompletableFuture<Long> getAndIncrement() {
- return asyncFuture(delegateCounter.getAndIncrement());
- }
-
- @Override
- public CompletableFuture<Long> getAndAdd(long delta) {
- return asyncFuture(delegateCounter.getAndAdd(delta));
- }
-
- @Override
- public CompletableFuture<Long> addAndGet(long delta) {
- return asyncFuture(delegateCounter.addAndGet(delta));
- }
-
- @Override
- public CompletableFuture<Long> get() {
- return asyncFuture(delegateCounter.get());
- }
-
- @Override
- public CompletableFuture<Void> set(long value) {
- return asyncFuture(delegateCounter.set(value));
- }
-
- @Override
- public CompletableFuture<Boolean> compareAndSet(long expectedValue, long updateValue) {
- return asyncFuture(delegateCounter.compareAndSet(expectedValue, updateValue));
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java
deleted file mode 100644
index a17a2f0..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-import org.onosproject.store.service.AsyncAtomicCounterMap;
-
-/**
- * {@link org.onosproject.store.service.AsyncAtomicCounterMap} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingAsyncAtomicCounterMap<K>
- extends ExecutingDistributedPrimitive implements AsyncAtomicCounterMap<K> {
- private final AsyncAtomicCounterMap<K> delegateMap;
-
- public ExecutingAsyncAtomicCounterMap(
- AsyncAtomicCounterMap<K> delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateMap, orderedExecutor, threadPoolExecutor);
- this.delegateMap = delegateMap;
- }
-
- @Override
- public CompletableFuture<Long> incrementAndGet(K key) {
- return asyncFuture(delegateMap.incrementAndGet(key));
- }
-
- @Override
- public CompletableFuture<Long> decrementAndGet(K key) {
- return asyncFuture(delegateMap.decrementAndGet(key));
- }
-
- @Override
- public CompletableFuture<Long> getAndIncrement(K key) {
- return asyncFuture(delegateMap.getAndIncrement(key));
- }
-
- @Override
- public CompletableFuture<Long> getAndDecrement(K key) {
- return asyncFuture(delegateMap.getAndDecrement(key));
- }
-
- @Override
- public CompletableFuture<Long> addAndGet(K key, long delta) {
- return asyncFuture(delegateMap.addAndGet(key, delta));
- }
-
- @Override
- public CompletableFuture<Long> getAndAdd(K key, long delta) {
- return asyncFuture(delegateMap.getAndAdd(key, delta));
- }
-
- @Override
- public CompletableFuture<Long> get(K key) {
- return asyncFuture(delegateMap.get(key));
- }
-
- @Override
- public CompletableFuture<Long> put(K key, long newValue) {
- return asyncFuture(delegateMap.put(key, newValue));
- }
-
- @Override
- public CompletableFuture<Long> putIfAbsent(K key, long newValue) {
- return asyncFuture(delegateMap.putIfAbsent(key, newValue));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(K key, long expectedOldValue, long newValue) {
- return asyncFuture(delegateMap.replace(key, expectedOldValue, newValue));
- }
-
- @Override
- public CompletableFuture<Long> remove(K key) {
- return asyncFuture(delegateMap.remove(key));
- }
-
- @Override
- public CompletableFuture<Boolean> remove(K key, long value) {
- return asyncFuture(delegateMap.remove(key, value));
- }
-
- @Override
- public CompletableFuture<Integer> size() {
- return asyncFuture(delegateMap.size());
- }
-
- @Override
- public CompletableFuture<Boolean> isEmpty() {
- return asyncFuture(delegateMap.isEmpty());
- }
-
- @Override
- public CompletableFuture<Void> clear() {
- return asyncFuture(delegateMap.clear());
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java
deleted file mode 100644
index baf2b8a..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-import org.onosproject.store.service.AsyncAtomicIdGenerator;
-
-/**
- * {@link AsyncAtomicIdGenerator} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingAsyncAtomicIdGenerator extends ExecutingDistributedPrimitive implements AsyncAtomicIdGenerator {
- private final AsyncAtomicIdGenerator delegateIdGenerator;
-
- public ExecutingAsyncAtomicIdGenerator(
- AsyncAtomicIdGenerator delegateIdGenerator, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateIdGenerator, orderedExecutor, threadPoolExecutor);
- this.delegateIdGenerator = delegateIdGenerator;
- }
-
- @Override
- public CompletableFuture<Long> nextId() {
- return asyncFuture(delegateIdGenerator.nextId());
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java
deleted file mode 100644
index c8bba52..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-import com.google.common.collect.Maps;
-import org.onosproject.store.service.AsyncAtomicValue;
-import org.onosproject.store.service.AtomicValueEventListener;
-
-/**
- * {@link AsyncAtomicValue} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingAsyncAtomicValue<V> extends ExecutingDistributedPrimitive implements AsyncAtomicValue<V> {
- private final AsyncAtomicValue<V> delegateValue;
- private final Executor orderedExecutor;
- private final Map<AtomicValueEventListener<V>, AtomicValueEventListener<V>> listenerMap = Maps.newConcurrentMap();
-
- public ExecutingAsyncAtomicValue(
- AsyncAtomicValue<V> delegateValue, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateValue, orderedExecutor, threadPoolExecutor);
- this.delegateValue = delegateValue;
- this.orderedExecutor = orderedExecutor;
- }
-
- @Override
- public CompletableFuture<Boolean> compareAndSet(V expect, V update) {
- return asyncFuture(delegateValue.compareAndSet(expect, update));
- }
-
- @Override
- public CompletableFuture<V> get() {
- return asyncFuture(delegateValue.get());
- }
-
- @Override
- public CompletableFuture<V> getAndSet(V value) {
- return asyncFuture(delegateValue.getAndSet(value));
- }
-
- @Override
- public CompletableFuture<Void> set(V value) {
- return asyncFuture(delegateValue.set(value));
- }
-
- @Override
- public CompletableFuture<Void> addListener(AtomicValueEventListener<V> listener) {
- AtomicValueEventListener<V> wrappedListener = e -> orderedExecutor.execute(() -> listener.event(e));
- listenerMap.put(listener, wrappedListener);
- return asyncFuture(delegateValue.addListener(wrappedListener));
- }
-
- @Override
- public CompletableFuture<Void> removeListener(AtomicValueEventListener<V> listener) {
- AtomicValueEventListener<V> wrappedListener = listenerMap.remove(listener);
- if (wrappedListener != null) {
- return asyncFuture(delegateValue.removeListener(wrappedListener));
- }
- return CompletableFuture.completedFuture(null);
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java
deleted file mode 100644
index d955121..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.BiFunction;
-import java.util.function.Predicate;
-
-import org.onosproject.store.primitives.MapUpdate;
-import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.service.AsyncConsistentMap;
-import org.onosproject.store.service.MapEventListener;
-import org.onosproject.store.service.TransactionLog;
-import org.onosproject.store.service.Version;
-import org.onosproject.store.service.Versioned;
-
-/**
- * An {@link org.onosproject.store.service.AsyncConsistentMap} that completes asynchronous calls on a provided
- * {@link Executor}.
- */
-public class ExecutingAsyncConsistentMap<K, V>
- extends ExecutingDistributedPrimitive implements AsyncConsistentMap<K, V> {
- private final AsyncConsistentMap<K, V> delegateMap;
-
- public ExecutingAsyncConsistentMap(
- AsyncConsistentMap<K, V> delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateMap, orderedExecutor, threadPoolExecutor);
- this.delegateMap = delegateMap;
- }
-
- @Override
- public CompletableFuture<Integer> size() {
- return asyncFuture(delegateMap.size());
- }
-
- @Override
- public CompletableFuture<Boolean> containsKey(K key) {
- return asyncFuture(delegateMap.containsKey(key));
- }
-
- @Override
- public CompletableFuture<Boolean> containsValue(V value) {
- return asyncFuture(delegateMap.containsValue(value));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> get(K key) {
- return asyncFuture(delegateMap.get(key));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> getOrDefault(K key, V defaultValue) {
- return asyncFuture(delegateMap.getOrDefault(key, defaultValue));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> computeIf(
- K key, Predicate<? super V> condition, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
- return asyncFuture(delegateMap.computeIf(key, condition, remappingFunction));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> put(K key, V value) {
- return asyncFuture(delegateMap.put(key, value));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> putAndGet(K key, V value) {
- return asyncFuture(delegateMap.putAndGet(key, value));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> remove(K key) {
- return asyncFuture(delegateMap.remove(key));
- }
-
- @Override
- public CompletableFuture<Void> clear() {
- return asyncFuture(delegateMap.clear());
- }
-
- @Override
- public CompletableFuture<Set<K>> keySet() {
- return asyncFuture(delegateMap.keySet());
- }
-
- @Override
- public CompletableFuture<Collection<Versioned<V>>> values() {
- return asyncFuture(delegateMap.values());
- }
-
- @Override
- public CompletableFuture<Set<Map.Entry<K, Versioned<V>>>> entrySet() {
- return asyncFuture(delegateMap.entrySet());
- }
-
- @Override
- public CompletableFuture<Versioned<V>> putIfAbsent(K key, V value) {
- return asyncFuture(delegateMap.putIfAbsent(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> remove(K key, V value) {
- return asyncFuture(delegateMap.remove(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> remove(K key, long version) {
- return asyncFuture(delegateMap.remove(key, version));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> replace(K key, V value) {
- return asyncFuture(delegateMap.replace(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) {
- return asyncFuture(delegateMap.replace(key, oldValue, newValue));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(K key, long oldVersion, V newValue) {
- return asyncFuture(delegateMap.replace(key, oldVersion, newValue));
- }
-
- @Override
- public CompletableFuture<Version> begin(TransactionId transactionId) {
- return asyncFuture(delegateMap.begin(transactionId));
- }
-
- @Override
- public CompletableFuture<Boolean> prepare(TransactionLog<MapUpdate<K, V>> transactionLog) {
- return asyncFuture(delegateMap.prepare(transactionLog));
- }
-
- @Override
- public CompletableFuture<Void> commit(TransactionId transactionId) {
- return asyncFuture(delegateMap.commit(transactionId));
- }
-
- @Override
- public CompletableFuture<Void> rollback(TransactionId transactionId) {
- return asyncFuture(delegateMap.rollback(transactionId));
- }
-
- @Override
- public CompletableFuture<Boolean> prepareAndCommit(TransactionLog<MapUpdate<K, V>> transactionLog) {
- return asyncFuture(delegateMap.prepareAndCommit(transactionLog));
- }
-
- @Override
- public CompletableFuture<Void> addListener(MapEventListener<K, V> listener) {
- return addListener(listener);
- }
-
- @Override
- public CompletableFuture<Void> addListener(MapEventListener<K, V> listener, Executor executor) {
- return asyncFuture(delegateMap.addListener(listener, executor));
- }
-
- @Override
- public CompletableFuture<Void> removeListener(MapEventListener<K, V> listener) {
- return asyncFuture(delegateMap.removeListener(listener));
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java
deleted file mode 100644
index 2045586..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import com.google.common.collect.Multiset;
-import org.onosproject.store.service.AsyncConsistentMultimap;
-import org.onosproject.store.service.MultimapEventListener;
-import org.onosproject.store.service.Versioned;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-/**
- * {@link org.onosproject.store.service.AsyncConsistentMultimap} that executes asynchronous callbacks on a provided
- * {@link Executor}.
- */
-public class ExecutingAsyncConsistentMultimap<K, V>
- extends ExecutingDistributedPrimitive implements AsyncConsistentMultimap<K, V> {
- private final AsyncConsistentMultimap<K, V> delegateMap;
-
- public ExecutingAsyncConsistentMultimap(
- AsyncConsistentMultimap<K, V> delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateMap, orderedExecutor, threadPoolExecutor);
- this.delegateMap = delegateMap;
- }
-
- @Override
- public CompletableFuture<Integer> size() {
- return asyncFuture(delegateMap.size());
- }
-
- @Override
- public CompletableFuture<Boolean> isEmpty() {
- return asyncFuture(delegateMap.isEmpty());
- }
-
- @Override
- public CompletableFuture<Boolean> containsKey(K key) {
- return asyncFuture(delegateMap.containsKey(key));
- }
-
- @Override
- public CompletableFuture<Boolean> containsValue(V value) {
- return asyncFuture(delegateMap.containsValue(value));
- }
-
- @Override
- public CompletableFuture<Boolean> containsEntry(K key, V value) {
- return asyncFuture(delegateMap.containsEntry(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> put(K key, V value) {
- return asyncFuture(delegateMap.put(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> remove(K key, V value) {
- return asyncFuture(delegateMap.remove(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> removeAll(K key, Collection<? extends V> values) {
- return asyncFuture(delegateMap.removeAll(key, values));
- }
-
- @Override
- public CompletableFuture<Versioned<Collection<? extends V>>> removeAll(K key) {
- return asyncFuture(delegateMap.removeAll(key));
- }
-
- @Override
- public CompletableFuture<Boolean> putAll(K key, Collection<? extends V> values) {
- return asyncFuture(delegateMap.putAll(key, values));
- }
-
- @Override
- public CompletableFuture<Versioned<Collection<? extends V>>> replaceValues(K key, Collection<V> values) {
- return asyncFuture(delegateMap.replaceValues(key, values));
- }
-
- @Override
- public CompletableFuture<Void> clear() {
- return asyncFuture(delegateMap.clear());
- }
-
- @Override
- public CompletableFuture<Versioned<Collection<? extends V>>> get(K key) {
- return asyncFuture(delegateMap.get(key));
- }
-
- @Override
- public CompletableFuture<Set<K>> keySet() {
- return asyncFuture(delegateMap.keySet());
- }
-
- @Override
- public CompletableFuture<Multiset<K>> keys() {
- return asyncFuture(delegateMap.keys());
- }
-
- @Override
- public CompletableFuture<Multiset<V>> values() {
- return asyncFuture(delegateMap.values());
- }
-
- @Override
- public CompletableFuture<Collection<Map.Entry<K, V>>> entries() {
- return asyncFuture(delegateMap.entries());
- }
-
- @Override
- public CompletableFuture<Void> addListener(MultimapEventListener<K, V> listener, Executor executor) {
- return asyncFuture(delegateMap.addListener(listener, executor));
- }
-
- @Override
- public CompletableFuture<Void> removeListener(MultimapEventListener<K, V> listener) {
- return asyncFuture(delegateMap.removeListener(listener));
- }
-
- @Override
- public CompletableFuture<Map<K, Collection<V>>> asMap() {
- return asyncFuture(delegateMap.asMap());
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java
deleted file mode 100644
index 3a4fe85..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.NavigableSet;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.BiFunction;
-import java.util.function.Predicate;
-
-import org.onosproject.store.primitives.MapUpdate;
-import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.service.AsyncConsistentTreeMap;
-import org.onosproject.store.service.MapEventListener;
-import org.onosproject.store.service.TransactionLog;
-import org.onosproject.store.service.Version;
-import org.onosproject.store.service.Versioned;
-
-/**
- * {@link org.onosproject.store.service.AsyncConsistentTreeMap} that executes asynchronous callbacks on a provided
- * {@link Executor}.
- */
-public class ExecutingAsyncConsistentTreeMap<V>
- extends ExecutingDistributedPrimitive implements AsyncConsistentTreeMap<V> {
- private final AsyncConsistentTreeMap<V> delegateMap;
-
- public ExecutingAsyncConsistentTreeMap(
- AsyncConsistentTreeMap<V> delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateMap, orderedExecutor, threadPoolExecutor);
- this.delegateMap = delegateMap;
- }
-
- @Override
- public CompletableFuture<String> firstKey() {
- return asyncFuture(delegateMap.firstKey());
- }
-
- @Override
- public CompletableFuture<String> lastKey() {
- return asyncFuture(delegateMap.lastKey());
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> ceilingEntry(String key) {
- return asyncFuture(delegateMap.ceilingEntry(key));
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> floorEntry(String key) {
- return asyncFuture(delegateMap.floorEntry(key));
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> higherEntry(String key) {
- return asyncFuture(delegateMap.higherEntry(key));
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> lowerEntry(String key) {
- return asyncFuture(delegateMap.lowerEntry(key));
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> firstEntry() {
- return asyncFuture(delegateMap.firstEntry());
- }
-
- @Override
- public CompletableFuture<Integer> size() {
- return asyncFuture(delegateMap.size());
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> lastEntry() {
- return asyncFuture(delegateMap.lastEntry());
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> pollFirstEntry() {
- return asyncFuture(delegateMap.pollFirstEntry());
- }
-
- @Override
- public CompletableFuture<Boolean> containsKey(String key) {
- return asyncFuture(delegateMap.containsKey(key));
- }
-
- @Override
- public CompletableFuture<Map.Entry<String, Versioned<V>>> pollLastEntry() {
- return asyncFuture(delegateMap.pollLastEntry());
- }
-
- @Override
- public CompletableFuture<String> lowerKey(String key) {
- return asyncFuture(delegateMap.lowerKey(key));
- }
-
- @Override
- public CompletableFuture<Boolean> containsValue(V value) {
- return asyncFuture(delegateMap.containsValue(value));
- }
-
- @Override
- public CompletableFuture<String> floorKey(String key) {
- return asyncFuture(delegateMap.floorKey(key));
- }
-
- @Override
- public CompletableFuture<String> ceilingKey(String key) {
- return asyncFuture(delegateMap.ceilingKey(key));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> get(String key) {
- return asyncFuture(delegateMap.get(key));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> getOrDefault(String key, V defaultValue) {
- return asyncFuture(delegateMap.getOrDefault(key, defaultValue));
- }
-
- @Override
- public CompletableFuture<String> higherKey(String key) {
- return asyncFuture(delegateMap.higherKey(key));
- }
-
- @Override
- public CompletableFuture<NavigableSet<String>> navigableKeySet() {
- return asyncFuture(delegateMap.navigableKeySet());
- }
-
- @Override
- public CompletableFuture<NavigableMap<String, V>> subMap(
- String upperKey, String lowerKey, boolean inclusiveUpper, boolean inclusiveLower) {
- return asyncFuture(delegateMap.subMap(upperKey, lowerKey, inclusiveUpper, inclusiveLower));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> computeIf(
- String key, Predicate<? super V> condition,
- BiFunction<? super String, ? super V, ? extends V> remappingFunction) {
- return asyncFuture(delegateMap.computeIf(key, condition, remappingFunction));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> put(String key, V value) {
- return asyncFuture(delegateMap.put(key, value));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> putAndGet(String key, V value) {
- return asyncFuture(delegateMap.putAndGet(key, value));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> remove(String key) {
- return asyncFuture(delegateMap.remove(key));
- }
-
- @Override
- public CompletableFuture<Void> clear() {
- return asyncFuture(delegateMap.clear());
- }
-
- @Override
- public CompletableFuture<Set<String>> keySet() {
- return asyncFuture(delegateMap.keySet());
- }
-
- @Override
- public CompletableFuture<Collection<Versioned<V>>> values() {
- return asyncFuture(delegateMap.values());
- }
-
- @Override
- public CompletableFuture<Set<Map.Entry<String, Versioned<V>>>> entrySet() {
- return asyncFuture(delegateMap.entrySet());
- }
-
- @Override
- public CompletableFuture<Versioned<V>> putIfAbsent(String key, V value) {
- return asyncFuture(delegateMap.putIfAbsent(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> remove(String key, V value) {
- return asyncFuture(delegateMap.remove(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> remove(String key, long version) {
- return asyncFuture(delegateMap.remove(key, version));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> replace(String key, V value) {
- return asyncFuture(delegateMap.replace(key, value));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(String key, V oldValue, V newValue) {
- return asyncFuture(delegateMap.replace(key, oldValue, newValue));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(String key, long oldVersion, V newValue) {
- return asyncFuture(delegateMap.replace(key, oldVersion, newValue));
- }
-
- @Override
- public CompletableFuture<Version> begin(TransactionId transactionId) {
- return asyncFuture(delegateMap.begin(transactionId));
- }
-
- @Override
- public CompletableFuture<Boolean> prepare(TransactionLog<MapUpdate<String, V>> transactionLog) {
- return asyncFuture(delegateMap.prepare(transactionLog));
- }
-
- @Override
- public CompletableFuture<Void> commit(TransactionId transactionId) {
- return asyncFuture(delegateMap.commit(transactionId));
- }
-
- @Override
- public CompletableFuture<Void> rollback(TransactionId transactionId) {
- return asyncFuture(delegateMap.rollback(transactionId));
- }
-
- @Override
- public CompletableFuture<Boolean> prepareAndCommit(TransactionLog<MapUpdate<String, V>> transactionLog) {
- return asyncFuture(delegateMap.prepareAndCommit(transactionLog));
- }
-
- @Override
- public CompletableFuture<Void> addListener(MapEventListener<String, V> listener) {
- return addListener(listener);
- }
-
- @Override
- public CompletableFuture<Void> addListener(MapEventListener<String, V> listener, Executor executor) {
- return asyncFuture(delegateMap.addListener(listener, executor));
- }
-
- @Override
- public CompletableFuture<Void> removeListener(MapEventListener<String, V> listener) {
- return asyncFuture(delegateMap.removeListener(listener));
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java
deleted file mode 100644
index f6fc3d1..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-import com.google.common.collect.Maps;
-import org.onosproject.store.service.AsyncDocumentTree;
-import org.onosproject.store.service.DocumentPath;
-import org.onosproject.store.service.DocumentTreeListener;
-import org.onosproject.store.service.Versioned;
-
-/**
- * {@link AsyncDocumentTree} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingAsyncDocumentTree<V> extends ExecutingDistributedPrimitive implements AsyncDocumentTree<V> {
- private final AsyncDocumentTree<V> delegateTree;
- private final Executor orderedExecutor;
- private final Map<DocumentTreeListener<V>, DocumentTreeListener<V>> listenerMap = Maps.newConcurrentMap();
-
- public ExecutingAsyncDocumentTree(
- AsyncDocumentTree<V> delegateTree, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateTree, orderedExecutor, threadPoolExecutor);
- this.delegateTree = delegateTree;
- this.orderedExecutor = orderedExecutor;
- }
-
- @Override
- public DocumentPath root() {
- return delegateTree.root();
- }
-
- @Override
- public CompletableFuture<Map<String, Versioned<V>>> getChildren(DocumentPath path) {
- return asyncFuture(delegateTree.getChildren(path));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> get(DocumentPath path) {
- return asyncFuture(delegateTree.get(path));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> set(DocumentPath path, V value) {
- return asyncFuture(delegateTree.set(path, value));
- }
-
- @Override
- public CompletableFuture<Boolean> create(DocumentPath path, V value) {
- return asyncFuture(delegateTree.create(path, value));
- }
-
- @Override
- public CompletableFuture<Boolean> createRecursive(DocumentPath path, V value) {
- return asyncFuture(delegateTree.createRecursive(path, value));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(DocumentPath path, V newValue, long version) {
- return asyncFuture(delegateTree.replace(path, newValue, version));
- }
-
- @Override
- public CompletableFuture<Boolean> replace(DocumentPath path, V newValue, V currentValue) {
- return asyncFuture(delegateTree.replace(path, newValue, currentValue));
- }
-
- @Override
- public CompletableFuture<Versioned<V>> removeNode(DocumentPath path) {
- return asyncFuture(delegateTree.removeNode(path));
- }
-
- @Override
- public CompletableFuture<Void> addListener(DocumentPath path, DocumentTreeListener<V> listener) {
- DocumentTreeListener<V> wrappedListener = e -> orderedExecutor.execute(() -> listener.event(e));
- listenerMap.put(listener, wrappedListener);
- return asyncFuture(delegateTree.addListener(path, wrappedListener));
- }
-
- @Override
- public CompletableFuture<Void> removeListener(DocumentTreeListener<V> listener) {
- DocumentTreeListener<V> wrappedListener = listenerMap.remove(listener);
- if (wrappedListener != null) {
- return asyncFuture(delegateTree.removeListener(wrappedListener));
- }
- return CompletableFuture.completedFuture(null);
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java
deleted file mode 100644
index ba7cb81..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-import com.google.common.collect.Maps;
-import org.onosproject.cluster.Leadership;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.event.Change;
-import org.onosproject.store.service.AsyncLeaderElector;
-
-/**
- * {@link AsyncLeaderElector} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingAsyncLeaderElector extends ExecutingDistributedPrimitive implements AsyncLeaderElector {
- private final AsyncLeaderElector delegateElector;
- private final Executor orderedExecutor;
- private final Map<Consumer<Change<Leadership>>, Consumer<Change<Leadership>>> listenerMap = Maps.newConcurrentMap();
-
- public ExecutingAsyncLeaderElector(
- AsyncLeaderElector delegateElector, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateElector, orderedExecutor, threadPoolExecutor);
- this.delegateElector = delegateElector;
- this.orderedExecutor = orderedExecutor;
- }
-
- @Override
- public CompletableFuture<Leadership> run(String topic, NodeId nodeId) {
- return asyncFuture(delegateElector.run(topic, nodeId));
- }
-
- @Override
- public CompletableFuture<Void> withdraw(String topic) {
- return asyncFuture(delegateElector.withdraw(topic));
- }
-
- @Override
- public CompletableFuture<Boolean> anoint(String topic, NodeId nodeId) {
- return asyncFuture(delegateElector.anoint(topic, nodeId));
- }
-
- @Override
- public CompletableFuture<Void> evict(NodeId nodeId) {
- return asyncFuture(delegateElector.evict(nodeId));
- }
-
- @Override
- public CompletableFuture<Boolean> promote(String topic, NodeId nodeId) {
- return asyncFuture(delegateElector.promote(topic, nodeId));
- }
-
- @Override
- public CompletableFuture<Leadership> getLeadership(String topic) {
- return asyncFuture(delegateElector.getLeadership(topic));
- }
-
- @Override
- public CompletableFuture<Map<String, Leadership>> getLeaderships() {
- return asyncFuture(delegateElector.getLeaderships());
- }
-
- @Override
- public CompletableFuture<Void> addChangeListener(Consumer<Change<Leadership>> listener) {
- Consumer<Change<Leadership>> wrappedListener = e -> orderedExecutor.execute(() -> listener.accept(e));
- listenerMap.put(listener, wrappedListener);
- return asyncFuture(delegateElector.addChangeListener(wrappedListener));
- }
-
- @Override
- public CompletableFuture<Void> removeChangeListener(Consumer<Change<Leadership>> listener) {
- Consumer<Change<Leadership>> wrappedListener = listenerMap.remove(listener);
- if (wrappedListener != null) {
- return asyncFuture(delegateElector.removeChangeListener(wrappedListener));
- }
- return CompletableFuture.completedFuture(null);
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java
deleted file mode 100644
index 836a682..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-import com.google.common.collect.Maps;
-import org.onlab.util.Tools;
-import org.onosproject.store.service.DistributedPrimitive;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Base class for primitives that delegate asynchronous callbacks to a user provided {@link Executor}.
- */
-public abstract class ExecutingDistributedPrimitive
- extends DelegatingDistributedPrimitive {
- private final DistributedPrimitive primitive;
- private final Executor orderedExecutor;
- private final Executor threadPoolExecutor;
- private final Map<Consumer<Status>, Consumer<Status>> listenerMap = Maps.newConcurrentMap();
-
- protected ExecutingDistributedPrimitive(
- DistributedPrimitive primitive, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(primitive);
- this.primitive = primitive;
- this.orderedExecutor = checkNotNull(orderedExecutor);
- this.threadPoolExecutor = checkNotNull(threadPoolExecutor);
- }
-
- /**
- * Creates a future to be completed asynchronously on the provided ordered and thread pool executors.
- *
- * @param future the future to be completed asynchronously
- * @param <T> future result type
- * @return a new {@link CompletableFuture} to be completed asynchronously using the primitive thread model
- */
- protected <T> CompletableFuture<T> asyncFuture(CompletableFuture<T> future) {
- return Tools.orderedFuture(future, orderedExecutor, threadPoolExecutor);
- }
-
- @Override
- public CompletableFuture<Void> destroy() {
- return asyncFuture(primitive.destroy());
- }
-
- @Override
- public void addStatusChangeListener(Consumer<DistributedPrimitive.Status> listener) {
- Consumer<DistributedPrimitive.Status> wrappedListener =
- status -> orderedExecutor.execute(() -> listener.accept(status));
- listenerMap.put(listener, wrappedListener);
- primitive.addStatusChangeListener(wrappedListener);
- }
-
- @Override
- public void removeStatusChangeListener(Consumer<DistributedPrimitive.Status> listener) {
- Consumer<DistributedPrimitive.Status> wrappedListener = listenerMap.remove(listener);
- if (wrappedListener != null) {
- primitive.removeStatusChangeListener(wrappedListener);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java
deleted file mode 100644
index e6290b8..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.util.Collection;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-import org.onosproject.store.service.AsyncAtomicValue;
-import org.onosproject.store.service.Task;
-import org.onosproject.store.service.WorkQueue;
-import org.onosproject.store.service.WorkQueueStats;
-
-/**
- * {@link AsyncAtomicValue} that executes asynchronous callbacks on a user provided
- * {@link Executor}.
- */
-public class ExecutingWorkQueue<E> extends ExecutingDistributedPrimitive implements WorkQueue<E> {
- private final WorkQueue<E> delegateQueue;
-
- public ExecutingWorkQueue(WorkQueue<E> delegateQueue, Executor orderedExecutor, Executor threadPoolExecutor) {
- super(delegateQueue, orderedExecutor, threadPoolExecutor);
- this.delegateQueue = delegateQueue;
- }
-
- @Override
- public CompletableFuture<Void> addMultiple(Collection<E> items) {
- return asyncFuture(delegateQueue.addMultiple(items));
- }
-
- @Override
- public CompletableFuture<Collection<Task<E>>> take(int maxItems) {
- return asyncFuture(delegateQueue.take(maxItems));
- }
-
- @Override
- public CompletableFuture<Void> complete(Collection<String> taskIds) {
- return asyncFuture(delegateQueue.complete(taskIds));
- }
-
- @Override
- public CompletableFuture<Void> registerTaskProcessor(
- Consumer<E> taskProcessor, int parallelism, Executor executor) {
- return asyncFuture(delegateQueue.registerTaskProcessor(taskProcessor, parallelism, executor));
- }
-
- @Override
- public CompletableFuture<Void> stopProcessing() {
- return asyncFuture(delegateQueue.stopProcessing());
- }
-
- @Override
- public CompletableFuture<WorkQueueStats> stats() {
- return asyncFuture(delegateQueue.stats());
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java
index 5564844..ea8d075 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java
@@ -40,8 +40,6 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -60,13 +58,12 @@
}
@Override
- public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
+ public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(String name, Serializer serializer) {
checkNotNull(name);
checkNotNull(serializer);
Map<PartitionId, AsyncConsistentMap<K, V>> maps =
Maps.transformValues(members,
- partition -> partition.newAsyncConsistentMap(name, serializer, executorSupplier));
+ partition -> partition.newAsyncConsistentMap(name, serializer));
Hasher<K> hasher = key -> {
int hashCode = Hashing.sha256().hashBytes(serializer.encode(key)).asInt();
return sortedMemberPartitionIds.get(Math.abs(hashCode) % members.size());
@@ -75,51 +72,46 @@
}
@Override
- public <V> AsyncConsistentTreeMap<V> newAsyncConsistentTreeMap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncConsistentTreeMap(name, serializer, executorSupplier);
+ public <V> AsyncConsistentTreeMap<V> newAsyncConsistentTreeMap(String name, Serializer serializer) {
+ return getCreator(name).newAsyncConsistentTreeMap(name, serializer);
}
@Override
- public <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncConsistentSetMultimap(name, serializer, executorSupplier);
+ public <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(String name, Serializer serializer) {
+ return getCreator(name).newAsyncConsistentSetMultimap(name, serializer);
}
@Override
- public <E> AsyncDistributedSet<E> newAsyncDistributedSet(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer, executorSupplier));
+ public <E> AsyncDistributedSet<E> newAsyncDistributedSet(String name, Serializer serializer) {
+ return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer));
}
@Override
- public <K> AsyncAtomicCounterMap<K> newAsyncAtomicCounterMap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncAtomicCounterMap(name, serializer, executorSupplier);
+ public <K> AsyncAtomicCounterMap<K> newAsyncAtomicCounterMap(String name, Serializer serializer) {
+ return getCreator(name).newAsyncAtomicCounterMap(name, serializer);
}
@Override
- public AsyncAtomicCounter newAsyncCounter(String name, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncCounter(name, executorSupplier);
+ public AsyncAtomicCounter newAsyncCounter(String name) {
+ return getCreator(name).newAsyncCounter(name);
}
@Override
- public AsyncAtomicIdGenerator newAsyncIdGenerator(String name, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncIdGenerator(name, executorSupplier);
+ public AsyncAtomicIdGenerator newAsyncIdGenerator(String name) {
+ return getCreator(name).newAsyncIdGenerator(name);
}
@Override
- public <V> AsyncAtomicValue<V> newAsyncAtomicValue(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncAtomicValue(name, serializer, executorSupplier);
+ public <V> AsyncAtomicValue<V> newAsyncAtomicValue(String name, Serializer serializer) {
+ return getCreator(name).newAsyncAtomicValue(name, serializer);
}
@Override
- public AsyncLeaderElector newAsyncLeaderElector(String name, Supplier<Executor> executorSupplier) {
+ public AsyncLeaderElector newAsyncLeaderElector(String name) {
checkNotNull(name);
Map<PartitionId, AsyncLeaderElector> leaderElectors =
Maps.transformValues(members,
- partition -> partition.newAsyncLeaderElector(name, executorSupplier));
+ partition -> partition.newAsyncLeaderElector(name));
Hasher<String> hasher = topic -> {
int hashCode = Hashing.sha256().hashString(topic, Charsets.UTF_8).asInt();
return sortedMemberPartitionIds.get(Math.abs(hashCode) % members.size());
@@ -128,14 +120,13 @@
}
@Override
- public <E> WorkQueue<E> newWorkQueue(String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return getCreator(name).newWorkQueue(name, serializer, executorSupplier);
+ public <E> WorkQueue<E> newWorkQueue(String name, Serializer serializer) {
+ return getCreator(name).newWorkQueue(name, serializer);
}
@Override
- public <V> AsyncDocumentTree<V> newAsyncDocumentTree(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return getCreator(name).newAsyncDocumentTree(name, serializer, executorSupplier);
+ public <V> AsyncDocumentTree<V> newAsyncDocumentTree(String name, Serializer serializer) {
+ return getCreator(name).newAsyncDocumentTree(name, serializer);
}
@Override
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java
deleted file mode 100644
index d541bb1..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onlab.util.Tools.maxPriority;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.net.ConnectException;
-import java.nio.channels.ClosedChannelException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Predicate;
-
-import org.onlab.util.Tools;
-import org.onosproject.store.service.StorageException;
-import org.slf4j.Logger;
-
-import com.google.common.base.Throwables;
-
-import io.atomix.catalyst.transport.TransportException;
-import io.atomix.copycat.Query;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.copycat.error.QueryException;
-import io.atomix.copycat.error.UnknownSessionException;
-import io.atomix.copycat.session.ClosedSessionException;
-
-/**
- * Custom {@code CopycatClient} for injecting additional logic that runs before/after operation submission.
- */
-public class OnosCopycatClient extends DelegatingCopycatClient {
-
- private final int maxRetries;
- private final long delayBetweenRetriesMillis;
- private final ScheduledExecutorService executor;
- private final Logger log = getLogger(getClass());
-
- private final Predicate<Throwable> retryableCheck = e -> e instanceof ConnectException
- || e instanceof TimeoutException
- || e instanceof TransportException
- || e instanceof ClosedChannelException
- || e instanceof QueryException
- || e instanceof UnknownSessionException
- || e instanceof ClosedSessionException
- || e instanceof StorageException.Unavailable;
-
- OnosCopycatClient(CopycatClient client, int maxRetries, long delayBetweenRetriesMillis) {
- super(client);
- this.maxRetries = maxRetries;
- this.delayBetweenRetriesMillis = delayBetweenRetriesMillis;
- this.executor = newSingleThreadScheduledExecutor(maxPriority(groupedThreads("OnosCopycat", "client", log)));
- }
-
- @Override
- public CompletableFuture<Void> close() {
- executor.shutdown();
- return super.close();
- }
-
- @Override
- public <T> CompletableFuture<T> submit(Query<T> query) {
- if (state() == State.CLOSED) {
- return Tools.exceptionalFuture(new StorageException.Unavailable());
- }
- CompletableFuture<T> future = new CompletableFuture<>();
- executor.execute(() -> submit(query, 1, future));
- return future;
- }
-
- private <T> void submit(Query<T> query, int attemptIndex, CompletableFuture<T> future) {
- client.submit(query).whenComplete((r, e) -> {
- if (e != null) {
- if (attemptIndex < maxRetries + 1 && retryableCheck.test(Throwables.getRootCause(e))) {
- log.debug("Retry attempt ({} of {}). Failure due to {}",
- attemptIndex, maxRetries, Throwables.getRootCause(e).getClass());
- executor.schedule(() ->
- submit(query, attemptIndex + 1, future), delayBetweenRetriesMillis, TimeUnit.MILLISECONDS);
- } else {
- future.completeExceptionally(e);
- }
- } else {
- future.complete(r);
- }
- });
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java
index bacda00..2201bb5 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java
@@ -16,19 +16,16 @@
package org.onosproject.store.primitives.impl;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.slf4j.LoggerFactory.getLogger;
-
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -46,7 +43,7 @@
import org.onosproject.cluster.PartitionDiff;
import org.onosproject.cluster.PartitionId;
import org.onosproject.event.AbstractListenerManager;
-import org.onosproject.store.cluster.messaging.MessagingService;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
import org.onosproject.store.primitives.DistributedPrimitiveCreator;
import org.onosproject.store.primitives.PartitionAdminService;
import org.onosproject.store.primitives.PartitionEvent;
@@ -56,11 +53,9 @@
import org.onosproject.store.service.PartitionInfo;
import org.slf4j.Logger;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-
import static org.onosproject.security.AppGuard.checkPermission;
import static org.onosproject.security.AppPermission.Type.PARTITION_READ;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Implementation of {@code PartitionService} and {@code PartitionAdminService}.
@@ -73,7 +68,7 @@
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected MessagingService messagingService;
+ protected ClusterCommunicationService clusterCommunicator;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterMetadataService metadataService;
@@ -84,9 +79,6 @@
private final Map<PartitionId, StoragePartition> partitions = Maps.newConcurrentMap();
private final AtomicReference<ClusterMetadata> currentClusterMetadata = new AtomicReference<>();
private final InternalClusterMetadataListener metadataListener = new InternalClusterMetadataListener();
- private final ExecutorService sharedPrimitiveExecutor = Executors.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors(),
- groupedThreads("onos/primitives", "primitive-events", log));
@Activate
public void activate() {
@@ -96,10 +88,8 @@
currentClusterMetadata.get()
.getPartitions()
.forEach(partition -> partitions.put(partition.getId(), new StoragePartition(partition,
- messagingService,
+ clusterCommunicator,
clusterService,
- CatalystSerializers.getSerializer(),
- sharedPrimitiveExecutor,
new File(System.getProperty("karaf.data") + "/partitions/" + partition.getId()))));
CompletableFuture<Void> openFuture = CompletableFuture.allOf(partitions.values()
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java
new file mode 100644
index 0000000..099e17a
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.impl;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.protocol.CloseSessionRequest;
+import io.atomix.protocols.raft.protocol.CloseSessionResponse;
+import io.atomix.protocols.raft.protocol.CommandRequest;
+import io.atomix.protocols.raft.protocol.CommandResponse;
+import io.atomix.protocols.raft.protocol.KeepAliveRequest;
+import io.atomix.protocols.raft.protocol.KeepAliveResponse;
+import io.atomix.protocols.raft.protocol.MetadataRequest;
+import io.atomix.protocols.raft.protocol.MetadataResponse;
+import io.atomix.protocols.raft.protocol.OpenSessionRequest;
+import io.atomix.protocols.raft.protocol.OpenSessionResponse;
+import io.atomix.protocols.raft.protocol.PublishRequest;
+import io.atomix.protocols.raft.protocol.QueryRequest;
+import io.atomix.protocols.raft.protocol.QueryResponse;
+import io.atomix.protocols.raft.protocol.RaftClientProtocol;
+import io.atomix.protocols.raft.protocol.ResetRequest;
+import io.atomix.protocols.raft.session.SessionId;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.PartitionId;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.service.Serializer;
+
+/**
+ * Raft client protocol that uses a cluster communicator.
+ */
+public class RaftClientCommunicator extends RaftCommunicator implements RaftClientProtocol {
+
+ public RaftClientCommunicator(
+ PartitionId partitionId,
+ Serializer serializer,
+ ClusterCommunicationService clusterCommunicator) {
+ super(new RaftMessageContext(String.format("partition-%d", partitionId.id())), serializer, clusterCommunicator);
+ }
+
+ @Override
+ public CompletableFuture<OpenSessionResponse> openSession(MemberId memberId, OpenSessionRequest request) {
+ return sendAndReceive(context.openSessionSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<CloseSessionResponse> closeSession(MemberId memberId, CloseSessionRequest request) {
+ return sendAndReceive(context.closeSessionSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<KeepAliveResponse> keepAlive(MemberId memberId, KeepAliveRequest request) {
+ return sendAndReceive(context.keepAliveSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<QueryResponse> query(MemberId memberId, QueryRequest request) {
+ return sendAndReceive(context.querySubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<CommandResponse> command(MemberId memberId, CommandRequest request) {
+ return sendAndReceive(context.commandSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<MetadataResponse> metadata(MemberId memberId, MetadataRequest request) {
+ return sendAndReceive(context.metadataSubject, request, memberId);
+ }
+
+ @Override
+ public void reset(Collection<MemberId> members, ResetRequest request) {
+ Set<NodeId> nodes = members.stream().map(m -> NodeId.nodeId(m.id())).collect(Collectors.toSet());
+ clusterCommunicator.multicast(
+ request,
+ context.resetSubject(request.session()),
+ serializer::encode,
+ nodes);
+ }
+
+ @Override
+ public void registerPublishListener(SessionId sessionId, Consumer<PublishRequest> listener, Executor executor) {
+ clusterCommunicator.addSubscriber(
+ context.publishSubject(sessionId.id()),
+ serializer::decode,
+ listener,
+ executor);
+ }
+
+ @Override
+ public void unregisterPublishListener(SessionId sessionId) {
+ clusterCommunicator.removeSubscriber(context.publishSubject(sessionId.id()));
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java
new file mode 100644
index 0000000..b708a34
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.impl;
+
+import java.net.ConnectException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import io.atomix.protocols.raft.RaftException;
+import io.atomix.protocols.raft.cluster.MemberId;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.cluster.messaging.MessageSubject;
+import org.onosproject.store.cluster.messaging.MessagingException;
+import org.onosproject.store.service.Serializer;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstract base class for Raft protocol client/server.
+ */
+public abstract class RaftCommunicator {
+ protected final RaftMessageContext context;
+ protected final Serializer serializer;
+ protected final ClusterCommunicationService clusterCommunicator;
+
+ public RaftCommunicator(
+ RaftMessageContext context,
+ Serializer serializer,
+ ClusterCommunicationService clusterCommunicator) {
+ this.context = checkNotNull(context, "context cannot be null");
+ this.serializer = checkNotNull(serializer, "serializer cannot be null");
+ this.clusterCommunicator = checkNotNull(clusterCommunicator, "clusterCommunicator cannot be null");
+ }
+
+ protected <T, U> CompletableFuture<U> sendAndReceive(MessageSubject subject, T request, MemberId memberId) {
+ CompletableFuture<U> future = new CompletableFuture<>();
+ clusterCommunicator.<T, U>sendAndReceive(
+ request, subject, serializer::encode, serializer::decode, NodeId.nodeId(memberId.id()))
+ .whenComplete((result, error) -> {
+ if (error == null) {
+ future.complete(result);
+ } else {
+ if (error instanceof CompletionException) {
+ error = error.getCause();
+ }
+ if (error instanceof MessagingException.NoRemoteHandler) {
+ error = new ConnectException(error.getMessage());
+ } else if (error instanceof MessagingException.RemoteHandlerFailure
+ || error instanceof MessagingException.ProtocolException) {
+ error = new RaftException.ProtocolException(error.getMessage());
+ }
+ future.completeExceptionally(error);
+ }
+ });
+ return future;
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java
new file mode 100644
index 0000000..2deb86a
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.impl;
+
+import org.onosproject.store.cluster.messaging.MessageSubject;
+
+/**
+ * Protocol message context.
+ */
+final class RaftMessageContext {
+ private final String prefix;
+ final MessageSubject openSessionSubject;
+ final MessageSubject closeSessionSubject;
+ final MessageSubject keepAliveSubject;
+ final MessageSubject querySubject;
+ final MessageSubject commandSubject;
+ final MessageSubject metadataSubject;
+ final MessageSubject joinSubject;
+ final MessageSubject leaveSubject;
+ final MessageSubject configureSubject;
+ final MessageSubject reconfigureSubject;
+ final MessageSubject installSubject;
+ final MessageSubject pollSubject;
+ final MessageSubject voteSubject;
+ final MessageSubject appendSubject;
+
+ RaftMessageContext(String prefix) {
+ this.prefix = prefix;
+ this.openSessionSubject = getSubject(prefix, "open");
+ this.closeSessionSubject = getSubject(prefix, "close");
+ this.keepAliveSubject = getSubject(prefix, "keep-alive");
+ this.querySubject = getSubject(prefix, "query");
+ this.commandSubject = getSubject(prefix, "command");
+ this.metadataSubject = getSubject(prefix, "metadata");
+ this.joinSubject = getSubject(prefix, "join");
+ this.leaveSubject = getSubject(prefix, "leave");
+ this.configureSubject = getSubject(prefix, "configure");
+ this.reconfigureSubject = getSubject(prefix, "reconfigure");
+ this.installSubject = getSubject(prefix, "install");
+ this.pollSubject = getSubject(prefix, "poll");
+ this.voteSubject = getSubject(prefix, "vote");
+ this.appendSubject = getSubject(prefix, "append");
+ }
+
+ private static MessageSubject getSubject(String prefix, String type) {
+ if (prefix == null) {
+ return new MessageSubject(type);
+ } else {
+ return new MessageSubject(String.format("%s-%s", prefix, type));
+ }
+ }
+
+ /**
+ * Returns the publish subject for the given session.
+ *
+ * @param sessionId the session for which to return the publish subject
+ * @return the publish subject for the given session
+ */
+ MessageSubject publishSubject(long sessionId) {
+ if (prefix == null) {
+ return new MessageSubject(String.format("publish-%d", sessionId));
+ } else {
+ return new MessageSubject(String.format("%s-publish-%d", prefix, sessionId));
+ }
+ }
+
+ /**
+ * Returns the reset subject for the given session.
+ *
+ * @param sessionId the session for which to return the reset subject
+ * @return the reset subject for the given session
+ */
+ MessageSubject resetSubject(long sessionId) {
+ if (prefix == null) {
+ return new MessageSubject(String.format("reset-%d", sessionId));
+ } else {
+ return new MessageSubject(String.format("%s-reset-%d", prefix, sessionId));
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java
new file mode 100644
index 0000000..b31810f
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.impl;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.protocol.AppendRequest;
+import io.atomix.protocols.raft.protocol.AppendResponse;
+import io.atomix.protocols.raft.protocol.CloseSessionRequest;
+import io.atomix.protocols.raft.protocol.CloseSessionResponse;
+import io.atomix.protocols.raft.protocol.CommandRequest;
+import io.atomix.protocols.raft.protocol.CommandResponse;
+import io.atomix.protocols.raft.protocol.ConfigureRequest;
+import io.atomix.protocols.raft.protocol.ConfigureResponse;
+import io.atomix.protocols.raft.protocol.InstallRequest;
+import io.atomix.protocols.raft.protocol.InstallResponse;
+import io.atomix.protocols.raft.protocol.JoinRequest;
+import io.atomix.protocols.raft.protocol.JoinResponse;
+import io.atomix.protocols.raft.protocol.KeepAliveRequest;
+import io.atomix.protocols.raft.protocol.KeepAliveResponse;
+import io.atomix.protocols.raft.protocol.LeaveRequest;
+import io.atomix.protocols.raft.protocol.LeaveResponse;
+import io.atomix.protocols.raft.protocol.MetadataRequest;
+import io.atomix.protocols.raft.protocol.MetadataResponse;
+import io.atomix.protocols.raft.protocol.OpenSessionRequest;
+import io.atomix.protocols.raft.protocol.OpenSessionResponse;
+import io.atomix.protocols.raft.protocol.PollRequest;
+import io.atomix.protocols.raft.protocol.PollResponse;
+import io.atomix.protocols.raft.protocol.PublishRequest;
+import io.atomix.protocols.raft.protocol.QueryRequest;
+import io.atomix.protocols.raft.protocol.QueryResponse;
+import io.atomix.protocols.raft.protocol.RaftServerProtocol;
+import io.atomix.protocols.raft.protocol.ReconfigureRequest;
+import io.atomix.protocols.raft.protocol.ReconfigureResponse;
+import io.atomix.protocols.raft.protocol.ResetRequest;
+import io.atomix.protocols.raft.protocol.VoteRequest;
+import io.atomix.protocols.raft.protocol.VoteResponse;
+import io.atomix.protocols.raft.session.SessionId;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.PartitionId;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.service.Serializer;
+
+/**
+ * Raft server protocol that uses a {@link ClusterCommunicationService}.
+ */
+public class RaftServerCommunicator extends RaftCommunicator implements RaftServerProtocol {
+
+ public RaftServerCommunicator(
+ PartitionId partitionId,
+ Serializer serializer,
+ ClusterCommunicationService clusterCommunicator) {
+ super(new RaftMessageContext(String.format("partition-%d", partitionId.id())), serializer, clusterCommunicator);
+ }
+
+ @Override
+ public CompletableFuture<OpenSessionResponse> openSession(MemberId memberId, OpenSessionRequest request) {
+ return sendAndReceive(context.openSessionSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<CloseSessionResponse> closeSession(MemberId memberId, CloseSessionRequest request) {
+ return sendAndReceive(context.closeSessionSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<KeepAliveResponse> keepAlive(MemberId memberId, KeepAliveRequest request) {
+ return sendAndReceive(context.keepAliveSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<QueryResponse> query(MemberId memberId, QueryRequest request) {
+ return sendAndReceive(context.querySubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<CommandResponse> command(MemberId memberId, CommandRequest request) {
+ return sendAndReceive(context.commandSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<MetadataResponse> metadata(MemberId memberId, MetadataRequest request) {
+ return sendAndReceive(context.metadataSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<JoinResponse> join(MemberId memberId, JoinRequest request) {
+ return sendAndReceive(context.joinSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<LeaveResponse> leave(MemberId memberId, LeaveRequest request) {
+ return sendAndReceive(context.leaveSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<ConfigureResponse> configure(MemberId memberId, ConfigureRequest request) {
+ return sendAndReceive(context.configureSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<ReconfigureResponse> reconfigure(MemberId memberId, ReconfigureRequest request) {
+ return sendAndReceive(context.reconfigureSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<InstallResponse> install(MemberId memberId, InstallRequest request) {
+ return sendAndReceive(context.installSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<PollResponse> poll(MemberId memberId, PollRequest request) {
+ return sendAndReceive(context.pollSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<VoteResponse> vote(MemberId memberId, VoteRequest request) {
+ return sendAndReceive(context.voteSubject, request, memberId);
+ }
+
+ @Override
+ public CompletableFuture<AppendResponse> append(MemberId memberId, AppendRequest request) {
+ return sendAndReceive(context.appendSubject, request, memberId);
+ }
+
+ @Override
+ public void publish(MemberId memberId, PublishRequest request) {
+ clusterCommunicator.unicast(request,
+ context.publishSubject(request.session()), serializer::encode, NodeId.nodeId(memberId.id()));
+ }
+
+ @Override
+ public void registerOpenSessionHandler(
+ Function<OpenSessionRequest, CompletableFuture<OpenSessionResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.openSessionSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterOpenSessionHandler() {
+ clusterCommunicator.removeSubscriber(context.openSessionSubject);
+ }
+
+ @Override
+ public void registerCloseSessionHandler(
+ Function<CloseSessionRequest, CompletableFuture<CloseSessionResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.closeSessionSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterCloseSessionHandler() {
+ clusterCommunicator.removeSubscriber(context.closeSessionSubject);
+ }
+
+ @Override
+ public void registerKeepAliveHandler(Function<KeepAliveRequest, CompletableFuture<KeepAliveResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.keepAliveSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterKeepAliveHandler() {
+ clusterCommunicator.removeSubscriber(context.keepAliveSubject);
+ }
+
+ @Override
+ public void registerQueryHandler(Function<QueryRequest, CompletableFuture<QueryResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.querySubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterQueryHandler() {
+ clusterCommunicator.removeSubscriber(context.querySubject);
+ }
+
+ @Override
+ public void registerCommandHandler(Function<CommandRequest, CompletableFuture<CommandResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.commandSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterCommandHandler() {
+ clusterCommunicator.removeSubscriber(context.commandSubject);
+ }
+
+ @Override
+ public void registerMetadataHandler(Function<MetadataRequest, CompletableFuture<MetadataResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.metadataSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterMetadataHandler() {
+ clusterCommunicator.removeSubscriber(context.metadataSubject);
+ }
+
+ @Override
+ public void registerJoinHandler(Function<JoinRequest, CompletableFuture<JoinResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.joinSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterJoinHandler() {
+ clusterCommunicator.removeSubscriber(context.joinSubject);
+ }
+
+ @Override
+ public void registerLeaveHandler(Function<LeaveRequest, CompletableFuture<LeaveResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.leaveSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterLeaveHandler() {
+ clusterCommunicator.removeSubscriber(context.leaveSubject);
+ }
+
+ @Override
+ public void registerConfigureHandler(Function<ConfigureRequest, CompletableFuture<ConfigureResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.configureSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterConfigureHandler() {
+ clusterCommunicator.removeSubscriber(context.configureSubject);
+ }
+
+ @Override
+ public void registerReconfigureHandler(
+ Function<ReconfigureRequest, CompletableFuture<ReconfigureResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.reconfigureSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterReconfigureHandler() {
+ clusterCommunicator.removeSubscriber(context.reconfigureSubject);
+ }
+
+ @Override
+ public void registerInstallHandler(Function<InstallRequest, CompletableFuture<InstallResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.installSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterInstallHandler() {
+ clusterCommunicator.removeSubscriber(context.installSubject);
+ }
+
+ @Override
+ public void registerPollHandler(Function<PollRequest, CompletableFuture<PollResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.pollSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterPollHandler() {
+ clusterCommunicator.removeSubscriber(context.pollSubject);
+ }
+
+ @Override
+ public void registerVoteHandler(Function<VoteRequest, CompletableFuture<VoteResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.voteSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterVoteHandler() {
+ clusterCommunicator.removeSubscriber(context.voteSubject);
+ }
+
+ @Override
+ public void registerAppendHandler(Function<AppendRequest, CompletableFuture<AppendResponse>> handler) {
+ clusterCommunicator.addSubscriber(context.appendSubject, serializer::decode, handler, serializer::encode);
+ }
+
+ @Override
+ public void unregisterAppendHandler() {
+ clusterCommunicator.removeSubscriber(context.appendSubject);
+ }
+
+ @Override
+ public void registerResetListener(SessionId sessionId, Consumer<ResetRequest> listener, Executor executor) {
+ clusterCommunicator.addSubscriber(context.resetSubject(sessionId.id()), serializer::decode, listener, executor);
+ }
+
+ @Override
+ public void unregisterResetListener(SessionId sessionId) {
+ clusterCommunicator.removeSubscriber(context.resetSubject(sessionId.id()));
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java
new file mode 100644
index 0000000..2db6aff
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.impl;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+
+import io.atomix.protocols.raft.RaftError;
+import io.atomix.protocols.raft.ReadConsistency;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.cluster.RaftMember;
+import io.atomix.protocols.raft.cluster.impl.DefaultRaftMember;
+import io.atomix.protocols.raft.event.RaftEvent;
+import io.atomix.protocols.raft.event.impl.DefaultEventType;
+import io.atomix.protocols.raft.operation.OperationType;
+import io.atomix.protocols.raft.operation.RaftOperation;
+import io.atomix.protocols.raft.operation.impl.DefaultOperationId;
+import io.atomix.protocols.raft.protocol.AppendRequest;
+import io.atomix.protocols.raft.protocol.AppendResponse;
+import io.atomix.protocols.raft.protocol.CloseSessionRequest;
+import io.atomix.protocols.raft.protocol.CloseSessionResponse;
+import io.atomix.protocols.raft.protocol.CommandRequest;
+import io.atomix.protocols.raft.protocol.CommandResponse;
+import io.atomix.protocols.raft.protocol.ConfigureRequest;
+import io.atomix.protocols.raft.protocol.ConfigureResponse;
+import io.atomix.protocols.raft.protocol.InstallRequest;
+import io.atomix.protocols.raft.protocol.InstallResponse;
+import io.atomix.protocols.raft.protocol.JoinRequest;
+import io.atomix.protocols.raft.protocol.JoinResponse;
+import io.atomix.protocols.raft.protocol.KeepAliveRequest;
+import io.atomix.protocols.raft.protocol.KeepAliveResponse;
+import io.atomix.protocols.raft.protocol.LeaveRequest;
+import io.atomix.protocols.raft.protocol.LeaveResponse;
+import io.atomix.protocols.raft.protocol.MetadataRequest;
+import io.atomix.protocols.raft.protocol.MetadataResponse;
+import io.atomix.protocols.raft.protocol.OpenSessionRequest;
+import io.atomix.protocols.raft.protocol.OpenSessionResponse;
+import io.atomix.protocols.raft.protocol.PollRequest;
+import io.atomix.protocols.raft.protocol.PollResponse;
+import io.atomix.protocols.raft.protocol.PublishRequest;
+import io.atomix.protocols.raft.protocol.QueryRequest;
+import io.atomix.protocols.raft.protocol.QueryResponse;
+import io.atomix.protocols.raft.protocol.RaftResponse;
+import io.atomix.protocols.raft.protocol.ReconfigureRequest;
+import io.atomix.protocols.raft.protocol.ReconfigureResponse;
+import io.atomix.protocols.raft.protocol.ResetRequest;
+import io.atomix.protocols.raft.protocol.VoteRequest;
+import io.atomix.protocols.raft.protocol.VoteResponse;
+import io.atomix.protocols.raft.session.RaftSessionMetadata;
+import io.atomix.protocols.raft.session.SessionId;
+import io.atomix.protocols.raft.storage.log.entry.CloseSessionEntry;
+import io.atomix.protocols.raft.storage.log.entry.CommandEntry;
+import io.atomix.protocols.raft.storage.log.entry.ConfigurationEntry;
+import io.atomix.protocols.raft.storage.log.entry.InitializeEntry;
+import io.atomix.protocols.raft.storage.log.entry.KeepAliveEntry;
+import io.atomix.protocols.raft.storage.log.entry.MetadataEntry;
+import io.atomix.protocols.raft.storage.log.entry.OpenSessionEntry;
+import io.atomix.protocols.raft.storage.log.entry.QueryEntry;
+import io.atomix.protocols.raft.storage.system.Configuration;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapEvents;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapEvents;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapEvents;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixCounterOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeEvents;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorEvents;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueEvents;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Storage serializer namespaces.
+ */
+public final class StorageNamespaces {
+
+ /**
+ * Raft protocol namespace.
+ */
+ public static final KryoNamespace RAFT_PROTOCOL = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(OpenSessionRequest.class)
+ .register(OpenSessionResponse.class)
+ .register(CloseSessionRequest.class)
+ .register(CloseSessionResponse.class)
+ .register(KeepAliveRequest.class)
+ .register(KeepAliveResponse.class)
+ .register(QueryRequest.class)
+ .register(QueryResponse.class)
+ .register(CommandRequest.class)
+ .register(CommandResponse.class)
+ .register(MetadataRequest.class)
+ .register(MetadataResponse.class)
+ .register(JoinRequest.class)
+ .register(JoinResponse.class)
+ .register(LeaveRequest.class)
+ .register(LeaveResponse.class)
+ .register(ConfigureRequest.class)
+ .register(ConfigureResponse.class)
+ .register(ReconfigureRequest.class)
+ .register(ReconfigureResponse.class)
+ .register(InstallRequest.class)
+ .register(InstallResponse.class)
+ .register(PollRequest.class)
+ .register(PollResponse.class)
+ .register(VoteRequest.class)
+ .register(VoteResponse.class)
+ .register(AppendRequest.class)
+ .register(AppendResponse.class)
+ .register(PublishRequest.class)
+ .register(ResetRequest.class)
+ .register(RaftResponse.Status.class)
+ .register(RaftError.class)
+ .register(RaftError.Type.class)
+ .register(ReadConsistency.class)
+ .register(RaftSessionMetadata.class)
+ .register(CloseSessionEntry.class)
+ .register(CommandEntry.class)
+ .register(ConfigurationEntry.class)
+ .register(InitializeEntry.class)
+ .register(KeepAliveEntry.class)
+ .register(MetadataEntry.class)
+ .register(OpenSessionEntry.class)
+ .register(QueryEntry.class)
+ .register(RaftOperation.class)
+ .register(RaftEvent.class)
+ .register(DefaultEventType.class)
+ .register(DefaultOperationId.class)
+ .register(OperationType.class)
+ .register(ReadConsistency.class)
+ .register(ArrayList.class)
+ .register(LinkedList.class)
+ .register(Collections.emptyList().getClass())
+ .register(HashSet.class)
+ .register(DefaultRaftMember.class)
+ .register(MemberId.class)
+ .register(SessionId.class)
+ .register(RaftMember.Type.class)
+ .register(RaftMember.Status.class)
+ .register(Instant.class)
+ .register(Configuration.class)
+ .register(AtomixAtomicCounterMapOperations.class)
+ .register(AtomixConsistentMapEvents.class)
+ .register(AtomixConsistentMapOperations.class)
+ .register(AtomixConsistentSetMultimapOperations.class)
+ .register(AtomixConsistentSetMultimapEvents.class)
+ .register(AtomixConsistentTreeMapEvents.class)
+ .register(AtomixConsistentTreeMapOperations.class)
+ .register(AtomixCounterOperations.class)
+ .register(AtomixDocumentTreeEvents.class)
+ .register(AtomixDocumentTreeOperations.class)
+ .register(AtomixLeaderElectorEvents.class)
+ .register(AtomixLeaderElectorOperations.class)
+ .register(AtomixWorkQueueEvents.class)
+ .register(AtomixWorkQueueOperations.class)
+ .build("RaftProtocol");
+
+ /**
+ * Raft storage namespace.
+ */
+ public static final KryoNamespace RAFT_STORAGE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 100)
+ .register(CloseSessionEntry.class)
+ .register(CommandEntry.class)
+ .register(ConfigurationEntry.class)
+ .register(InitializeEntry.class)
+ .register(KeepAliveEntry.class)
+ .register(MetadataEntry.class)
+ .register(OpenSessionEntry.class)
+ .register(QueryEntry.class)
+ .register(RaftOperation.class)
+ .register(ReadConsistency.class)
+ .register(ArrayList.class)
+ .register(HashSet.class)
+ .register(DefaultRaftMember.class)
+ .register(MemberId.class)
+ .register(RaftMember.Type.class)
+ .register(RaftMember.Status.class)
+ .register(Instant.class)
+ .register(Configuration.class)
+ .register(AtomixAtomicCounterMapOperations.class)
+ .register(AtomixConsistentMapOperations.class)
+ .register(AtomixConsistentSetMultimapOperations.class)
+ .register(AtomixConsistentTreeMapOperations.class)
+ .register(AtomixCounterOperations.class)
+ .register(AtomixDocumentTreeOperations.class)
+ .register(AtomixLeaderElectorOperations.class)
+ .register(AtomixWorkQueueOperations.class)
+ .build("RaftStorage");
+
+ private StorageNamespaces() {
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java
index e9261c8..051c95a 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java
@@ -15,31 +15,36 @@
*/
package org.onosproject.store.primitives.impl;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.resource.ResourceType;
-
import java.io.File;
import java.util.Collection;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.service.RaftService;
import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.cluster.Partition;
import org.onosproject.cluster.PartitionId;
-import org.onosproject.store.cluster.messaging.MessagingService;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMap;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElector;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapService;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapService;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapService;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapService;
+import org.onosproject.store.primitives.resources.impl.AtomixCounterService;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeService;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorService;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueService;
+import org.onosproject.store.service.DistributedPrimitive;
import org.onosproject.store.service.PartitionInfo;
-
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableSet;
+import org.onosproject.store.service.Serializer;
/**
* Storage partition.
@@ -47,32 +52,32 @@
public class StoragePartition implements Managed<StoragePartition> {
private final AtomicBoolean isOpened = new AtomicBoolean(false);
- private final Serializer serializer;
- private final Executor sharedExecutor;
- private final MessagingService messagingService;
- private final ClusterService clusterService;
+ private final ClusterCommunicationService clusterCommunicator;
private final File logFolder;
private Partition partition;
private NodeId localNodeId;
private StoragePartitionServer server;
private StoragePartitionClient client;
- public static final Collection<ResourceType> RESOURCE_TYPES = ImmutableSet.of(
- new ResourceType(AtomixLeaderElector.class),
- new ResourceType(AtomixConsistentMap.class));
+ public static final Map<String, Supplier<RaftService>> RAFT_SERVICES =
+ ImmutableMap.<String, Supplier<RaftService>>builder()
+ .put(DistributedPrimitive.Type.CONSISTENT_MAP.name(), AtomixConsistentMapService::new)
+ .put(DistributedPrimitive.Type.CONSISTENT_TREEMAP.name(), AtomixConsistentTreeMapService::new)
+ .put(DistributedPrimitive.Type.CONSISTENT_MULTIMAP.name(), AtomixConsistentSetMultimapService::new)
+ .put(DistributedPrimitive.Type.COUNTER_MAP.name(), AtomixAtomicCounterMapService::new)
+ .put(DistributedPrimitive.Type.COUNTER.name(), AtomixCounterService::new)
+ .put(DistributedPrimitive.Type.LEADER_ELECTOR.name(), AtomixLeaderElectorService::new)
+ .put(DistributedPrimitive.Type.WORK_QUEUE.name(), AtomixWorkQueueService::new)
+ .put(DistributedPrimitive.Type.DOCUMENT_TREE.name(), AtomixDocumentTreeService::new)
+ .build();
public StoragePartition(Partition partition,
- MessagingService messagingService,
+ ClusterCommunicationService clusterCommunicator,
ClusterService clusterService,
- Serializer serializer,
- Executor sharedExecutor,
File logFolder) {
this.partition = partition;
- this.messagingService = messagingService;
- this.clusterService = clusterService;
+ this.clusterCommunicator = clusterCommunicator;
this.localNodeId = clusterService.getLocalNode().id();
- this.serializer = serializer;
- this.sharedExecutor = sharedExecutor;
this.logFolder = logFolder;
}
@@ -87,10 +92,14 @@
@Override
public CompletableFuture<Void> open() {
if (partition.getMembers().contains(localNodeId)) {
- openServer();
+ return openServer()
+ .thenCompose(v -> openClient())
+ .thenAccept(v -> isOpened.set(true))
+ .thenApply(v -> null);
}
- return openClient().thenAccept(v -> isOpened.set(true))
- .thenApply(v -> null);
+ return openClient()
+ .thenAccept(v -> isOpened.set(true))
+ .thenApply(v -> null);
}
@Override
@@ -117,11 +126,11 @@
}
/**
- * Returns the {@link Address addresses} of partition members.
- * @return partition member addresses
+ * Returns the {@link MemberId identifiers} of partition members.
+ * @return partition member identifiers
*/
- public Collection<Address> getMemberAddresses() {
- return Collections2.transform(partition.getMembers(), this::toAddress);
+ public Collection<MemberId> getMemberIds() {
+ return Collections2.transform(partition.getMembers(), n -> MemberId.from(n.id()));
}
/**
@@ -132,10 +141,12 @@
if (!partition.getMembers().contains(localNodeId) || server != null) {
return CompletableFuture.completedFuture(null);
}
- StoragePartitionServer server = new StoragePartitionServer(toAddress(localNodeId),
- this,
- serializer,
- () -> new CopycatTransport(partition.getId(), messagingService),
+ StoragePartitionServer server = new StoragePartitionServer(this,
+ MemberId.from(localNodeId.id()),
+ () -> new RaftServerCommunicator(
+ partition.getId(),
+ Serializer.using(StorageNamespaces.RAFT_PROTOCOL),
+ clusterCommunicator),
logFolder);
return server.open().thenRun(() -> this.server = server);
}
@@ -149,19 +160,24 @@
.stream()
.filter(nodeId -> !nodeId.equals(localNodeId))
.collect(Collectors.toSet());
- StoragePartitionServer server = new StoragePartitionServer(toAddress(localNodeId),
- this,
- serializer,
- () -> new CopycatTransport(partition.getId(), messagingService),
+ StoragePartitionServer server = new StoragePartitionServer(this,
+ MemberId.from(localNodeId.id()),
+ () -> new RaftServerCommunicator(
+ partition.getId(),
+ Serializer.using(StorageNamespaces.RAFT_PROTOCOL),
+ clusterCommunicator),
logFolder);
- return server.join(Collections2.transform(otherMembers, this::toAddress)).thenRun(() -> this.server = server);
+ return server.join(Collections2.transform(otherMembers, n -> MemberId.from(n.id())))
+ .thenRun(() -> this.server = server);
}
private CompletableFuture<StoragePartitionClient> openClient() {
client = new StoragePartitionClient(this,
- serializer,
- new CopycatTransport(partition.getId(), messagingService),
- sharedExecutor);
+ MemberId.from(localNodeId.id()),
+ new RaftClientCommunicator(
+ partition.getId(),
+ Serializer.using(StorageNamespaces.RAFT_PROTOCOL),
+ clusterCommunicator));
return client.open().thenApply(v -> client);
}
@@ -185,11 +201,6 @@
return CompletableFuture.completedFuture(null);
}
- private Address toAddress(NodeId nodeId) {
- ControllerNode node = clusterService.getNode(nodeId);
- return new Address(node.ip().toString(), node.tcpPort());
- }
-
/**
* Returns the partition information if this partition is locally managed i.e.
* this node is a active member of the partition.
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
index d97868c..b55efc4 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
@@ -15,30 +15,19 @@
*/
package org.onosproject.store.primitives.impl;
-import java.util.Collection;
+import java.time.Duration;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
+import java.util.stream.Collectors;
import com.google.common.base.Suppliers;
-import io.atomix.AtomixClient;
-import io.atomix.catalyst.transport.Transport;
-import io.atomix.copycat.client.ConnectionStrategies;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.copycat.client.CopycatClient.State;
-import io.atomix.copycat.client.RecoveryStrategies;
-import io.atomix.copycat.client.ServerSelectionStrategies;
-import io.atomix.manager.ResourceClient;
-import io.atomix.manager.ResourceManagerException;
-import io.atomix.manager.util.ResourceManagerTypeResolver;
-import io.atomix.resource.ResourceRegistry;
-import io.atomix.resource.ResourceType;
-import io.atomix.variables.DistributedLong;
+import io.atomix.protocols.raft.RaftClient;
+import io.atomix.protocols.raft.ReadConsistency;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.protocol.RaftClientProtocol;
+import io.atomix.protocols.raft.proxy.CommunicationStrategy;
+import io.atomix.protocols.raft.session.RaftSessionMetadata;
import org.onlab.util.HexString;
-import org.onlab.util.OrderedExecutor;
import org.onosproject.store.primitives.DistributedPrimitiveCreator;
import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMap;
import org.onosproject.store.primitives.resources.impl.AtomixConsistentMap;
@@ -60,7 +49,7 @@
import org.onosproject.store.service.AsyncDistributedSet;
import org.onosproject.store.service.AsyncDocumentTree;
import org.onosproject.store.service.AsyncLeaderElector;
-import org.onosproject.store.service.DistributedPrimitive.Status;
+import org.onosproject.store.service.DistributedPrimitive;
import org.onosproject.store.service.PartitionClientInfo;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.WorkQueue;
@@ -76,55 +65,33 @@
private final Logger log = getLogger(getClass());
private final StoragePartition partition;
- private final Transport transport;
- private final io.atomix.catalyst.serializer.Serializer serializer;
- private final Executor sharedExecutor;
- private AtomixClient client;
- private ResourceClient resourceClient;
+ private final MemberId localMemberId;
+ private final RaftClientProtocol protocol;
+ private RaftClient client;
private static final String ATOMIC_VALUES_CONSISTENT_MAP_NAME = "onos-atomic-values";
private final com.google.common.base.Supplier<AsyncConsistentMap<String, byte[]>> onosAtomicValuesMap =
Suppliers.memoize(() -> newAsyncConsistentMap(ATOMIC_VALUES_CONSISTENT_MAP_NAME,
Serializer.using(KryoNamespaces.BASIC)));
- Function<State, Status> mapper = state -> {
- switch (state) {
- case CONNECTED:
- return Status.ACTIVE;
- case SUSPENDED:
- return Status.SUSPENDED;
- case CLOSED:
- return Status.INACTIVE;
- default:
- throw new IllegalStateException("Unknown state " + state);
- }
- };
- public StoragePartitionClient(StoragePartition partition,
- io.atomix.catalyst.serializer.Serializer serializer,
- Transport transport,
- Executor sharedExecutor) {
+ public StoragePartitionClient(StoragePartition partition, MemberId localMemberId, RaftClientProtocol protocol) {
this.partition = partition;
- this.serializer = serializer;
- this.transport = transport;
- this.sharedExecutor = sharedExecutor;
+ this.localMemberId = localMemberId;
+ this.protocol = protocol;
}
@Override
public CompletableFuture<Void> open() {
synchronized (StoragePartitionClient.this) {
- resourceClient = newResourceClient(transport,
- serializer.clone(),
- StoragePartition.RESOURCE_TYPES);
- resourceClient.client().onStateChange(state -> log.debug("Partition {} client state"
- + " changed to {}", partition.getId(), state));
- client = new AtomixClient(resourceClient);
+ client = newRaftClient(protocol);
}
- return client.connect(partition.getMemberAddresses()).whenComplete((r, e) -> {
+ return client.connect(partition.getMemberIds()).whenComplete((r, e) -> {
if (e == null) {
log.info("Successfully started client for partition {}", partition.getId());
} else {
log.info("Failed to start client for partition {}", partition.getId(), e);
}
}).thenApply(v -> null);
+
}
@Override
@@ -132,25 +99,19 @@
return client != null ? client.close() : CompletableFuture.completedFuture(null);
}
- /**
- * Returns the executor provided by the given supplier or a serial executor if the supplier is {@code null}.
- *
- * @param executorSupplier the user-provided executor supplier
- * @return the executor
- */
- private Executor defaultExecutor(Supplier<Executor> executorSupplier) {
- return executorSupplier != null ? executorSupplier.get() : new OrderedExecutor(sharedExecutor);
- }
-
@Override
- public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- AtomixConsistentMap atomixConsistentMap = client.getResource(name, AtomixConsistentMap.class).join();
- Consumer<State> statusListener = state -> {
- atomixConsistentMap.statusChangeListeners()
- .forEach(listener -> listener.accept(mapper.apply(state)));
- };
- resourceClient.client().onStateChange(statusListener);
+ public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(String name, Serializer serializer) {
+ AtomixConsistentMap atomixConsistentMap =
+ new AtomixConsistentMap(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.CONSISTENT_MAP.name())
+ .withReadConsistency(ReadConsistency.SEQUENTIAL)
+ .withCommunicationStrategy(CommunicationStrategy.ANY)
+ .withTimeout(Duration.ofSeconds(30))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
AsyncConsistentMap<String, byte[]> rawMap =
new DelegatingAsyncConsistentMap<String, byte[]>(atomixConsistentMap) {
@@ -162,24 +123,27 @@
// We have to ensure serialization is done on the Copycat threads since Kryo is not thread safe.
AsyncConsistentMap<K, V> transcodedMap = DistributedPrimitives.newTranscodingMap(rawMap,
- key -> HexString.toHexString(serializer.encode(key)),
- string -> serializer.decode(HexString.fromHexString(string)),
- value -> value == null ? null : serializer.encode(value),
- bytes -> serializer.decode(bytes));
+ key -> HexString.toHexString(serializer.encode(key)),
+ string -> serializer.decode(HexString.fromHexString(string)),
+ value -> value == null ? null : serializer.encode(value),
+ bytes -> serializer.decode(bytes));
- return new ExecutingAsyncConsistentMap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor);
+ return transcodedMap;
}
@Override
- public <V> AsyncConsistentTreeMap<V> newAsyncConsistentTreeMap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
+ public <V> AsyncConsistentTreeMap<V> newAsyncConsistentTreeMap(String name, Serializer serializer) {
AtomixConsistentTreeMap atomixConsistentTreeMap =
- client.getResource(name, AtomixConsistentTreeMap.class).join();
- Consumer<State> statusListener = state -> {
- atomixConsistentTreeMap.statusChangeListeners()
- .forEach(listener -> listener.accept(mapper.apply(state)));
- };
- resourceClient.client().onStateChange(statusListener);
+ new AtomixConsistentTreeMap(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.CONSISTENT_TREEMAP.name())
+ .withReadConsistency(ReadConsistency.SEQUENTIAL)
+ .withCommunicationStrategy(CommunicationStrategy.ANY)
+ .withTimeout(Duration.ofSeconds(30))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
AsyncConsistentTreeMap<byte[]> rawMap =
new DelegatingAsyncConsistentTreeMap<byte[]>(atomixConsistentTreeMap) {
@@ -191,24 +155,26 @@
AsyncConsistentTreeMap<V> transcodedMap =
DistributedPrimitives.<V, byte[]>newTranscodingTreeMap(
- rawMap,
- value -> value == null ? null : serializer.encode(value),
- bytes -> serializer.decode(bytes));
+ rawMap,
+ value -> value == null ? null : serializer.encode(value),
+ bytes -> serializer.decode(bytes));
- return new ExecutingAsyncConsistentTreeMap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor);
+ return transcodedMap;
}
@Override
- public <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
+ public <K, V> AsyncConsistentMultimap<K, V> newAsyncConsistentSetMultimap(String name, Serializer serializer) {
AtomixConsistentSetMultimap atomixConsistentSetMultimap =
- client.getResource(name, AtomixConsistentSetMultimap.class)
- .join();
- Consumer<State> statusListener = state -> {
- atomixConsistentSetMultimap.statusChangeListeners()
- .forEach(listener -> listener.accept(mapper.apply(state)));
- };
- resourceClient.client().onStateChange(statusListener);
+ new AtomixConsistentSetMultimap(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.CONSISTENT_MULTIMAP.name())
+ .withReadConsistency(ReadConsistency.SEQUENTIAL)
+ .withCommunicationStrategy(CommunicationStrategy.ANY)
+ .withTimeout(Duration.ofSeconds(30))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
AsyncConsistentMultimap<String, byte[]> rawMap =
new DelegatingAsyncConsistentMultimap<String, byte[]>(
@@ -227,97 +193,136 @@
value -> serializer.encode(value),
bytes -> serializer.decode(bytes));
- return new ExecutingAsyncConsistentMultimap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor);
+ return transcodedMap;
}
@Override
- public <E> AsyncDistributedSet<E> newAsyncDistributedSet(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer, executorSupplier));
+ public <E> AsyncDistributedSet<E> newAsyncDistributedSet(String name, Serializer serializer) {
+ return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer));
}
@Override
- public <K> AsyncAtomicCounterMap<K> newAsyncAtomicCounterMap(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- AtomixAtomicCounterMap atomixAtomicCounterMap =
- client.getResource(name, AtomixAtomicCounterMap.class)
- .join();
+ public <K> AsyncAtomicCounterMap<K> newAsyncAtomicCounterMap(String name, Serializer serializer) {
+ AtomixAtomicCounterMap atomixAtomicCounterMap = new AtomixAtomicCounterMap(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.COUNTER_MAP.name())
+ .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE)
+ .withCommunicationStrategy(CommunicationStrategy.LEADER)
+ .withTimeout(Duration.ofSeconds(30))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
AsyncAtomicCounterMap<K> transcodedMap =
- DistributedPrimitives.<K, String>newTranscodingAtomicCounterMap(
- atomixAtomicCounterMap,
+ DistributedPrimitives.newTranscodingAtomicCounterMap(
+ atomixAtomicCounterMap,
key -> HexString.toHexString(serializer.encode(key)),
string -> serializer.decode(HexString.fromHexString(string)));
- return new ExecutingAsyncAtomicCounterMap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor);
+ return transcodedMap;
}
@Override
- public AsyncAtomicCounter newAsyncCounter(String name, Supplier<Executor> executorSupplier) {
- DistributedLong distributedLong = client.getLong(name).join();
- AsyncAtomicCounter asyncCounter = new AtomixCounter(name, distributedLong);
- return new ExecutingAsyncAtomicCounter(asyncCounter, defaultExecutor(executorSupplier), sharedExecutor);
+ public AsyncAtomicCounter newAsyncCounter(String name) {
+ return new AtomixCounter(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.COUNTER.name())
+ .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE)
+ .withCommunicationStrategy(CommunicationStrategy.LEADER)
+ .withTimeout(Duration.ofSeconds(30))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
}
@Override
- public AsyncAtomicIdGenerator newAsyncIdGenerator(String name, Supplier<Executor> executorSupplier) {
- DistributedLong distributedLong = client.getLong(name).join();
- AsyncAtomicIdGenerator asyncIdGenerator = new AtomixIdGenerator(name, distributedLong);
- return new ExecutingAsyncAtomicIdGenerator(asyncIdGenerator, defaultExecutor(executorSupplier), sharedExecutor);
+ public AsyncAtomicIdGenerator newAsyncIdGenerator(String name) {
+ return new AtomixIdGenerator(newAsyncCounter(name));
}
@Override
- public <V> AsyncAtomicValue<V> newAsyncAtomicValue(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- AsyncAtomicValue<V> asyncValue = new DefaultAsyncAtomicValue<>(name, serializer, onosAtomicValuesMap.get());
- return new ExecutingAsyncAtomicValue<>(asyncValue, defaultExecutor(executorSupplier), sharedExecutor);
+ public <V> AsyncAtomicValue<V> newAsyncAtomicValue(String name, Serializer serializer) {
+ return new DefaultAsyncAtomicValue<>(name, serializer, onosAtomicValuesMap.get());
}
@Override
- public <E> WorkQueue<E> newWorkQueue(String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- AtomixWorkQueue atomixWorkQueue = client.getResource(name, AtomixWorkQueue.class).join();
- WorkQueue<E> workQueue = new DefaultDistributedWorkQueue<>(atomixWorkQueue, serializer);
- return new ExecutingWorkQueue<>(workQueue, defaultExecutor(executorSupplier), sharedExecutor);
+ public <E> WorkQueue<E> newWorkQueue(String name, Serializer serializer) {
+ AtomixWorkQueue atomixWorkQueue = new AtomixWorkQueue(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.WORK_QUEUE.name())
+ .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE)
+ .withCommunicationStrategy(CommunicationStrategy.LEADER)
+ .withTimeout(Duration.ofSeconds(5))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
+ return new DefaultDistributedWorkQueue<>(atomixWorkQueue, serializer);
}
@Override
- public <V> AsyncDocumentTree<V> newAsyncDocumentTree(
- String name, Serializer serializer, Supplier<Executor> executorSupplier) {
- AtomixDocumentTree atomixDocumentTree = client.getResource(name, AtomixDocumentTree.class).join();
- AsyncDocumentTree<V> asyncDocumentTree = new DefaultDistributedDocumentTree<>(
- name, atomixDocumentTree, serializer);
- return new ExecutingAsyncDocumentTree<>(asyncDocumentTree, defaultExecutor(executorSupplier), sharedExecutor);
+ public <V> AsyncDocumentTree<V> newAsyncDocumentTree(String name, Serializer serializer) {
+ AtomixDocumentTree atomixDocumentTree = new AtomixDocumentTree(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.DOCUMENT_TREE.name())
+ .withReadConsistency(ReadConsistency.SEQUENTIAL)
+ .withCommunicationStrategy(CommunicationStrategy.ANY)
+ .withTimeout(Duration.ofSeconds(30))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
+ return new DefaultDistributedDocumentTree<>(name, atomixDocumentTree, serializer);
}
@Override
- public AsyncLeaderElector newAsyncLeaderElector(String name, Supplier<Executor> executorSupplier) {
- AtomixLeaderElector leaderElector = client.getResource(name, AtomixLeaderElector.class)
- .thenCompose(AtomixLeaderElector::setupCache)
- .join();
- Consumer<State> statusListener = state -> leaderElector.statusChangeListeners()
- .forEach(listener -> listener.accept(mapper.apply(state)));
- resourceClient.client().onStateChange(statusListener);
- return new ExecutingAsyncLeaderElector(leaderElector, defaultExecutor(executorSupplier), sharedExecutor);
+ public AsyncLeaderElector newAsyncLeaderElector(String name) {
+ AtomixLeaderElector leaderElector = new AtomixLeaderElector(client.newProxyBuilder()
+ .withName(name)
+ .withServiceType(DistributedPrimitive.Type.LEADER_ELECTOR.name())
+ .withReadConsistency(ReadConsistency.LINEARIZABLE)
+ .withCommunicationStrategy(CommunicationStrategy.LEADER)
+ .withTimeout(Duration.ofSeconds(5))
+ .withMaxRetries(5)
+ .build()
+ .open()
+ .join());
+ leaderElector.setupCache().join();
+ return leaderElector;
}
@Override
public Set<String> getAsyncConsistentMapNames() {
- return client.keys(AtomixConsistentMap.class).join();
+ return client.metadata().getSessions(DistributedPrimitive.Type.CONSISTENT_MAP.name())
+ .join()
+ .stream()
+ .map(RaftSessionMetadata::serviceName)
+ .collect(Collectors.toSet());
}
@Override
public Set<String> getAsyncAtomicCounterNames() {
- return client.keys(DistributedLong.class).join();
+ return client.metadata().getSessions(DistributedPrimitive.Type.COUNTER.name())
+ .join()
+ .stream()
+ .map(RaftSessionMetadata::serviceName)
+ .collect(Collectors.toSet());
}
@Override
public Set<String> getWorkQueueNames() {
- return client.keys(AtomixWorkQueue.class).join();
+ return client.metadata().getSessions(DistributedPrimitive.Type.WORK_QUEUE.name())
+ .join()
+ .stream()
+ .map(RaftSessionMetadata::serviceName)
+ .collect(Collectors.toSet());
}
@Override
public boolean isOpen() {
- return resourceClient.client().state() != State.CLOSED;
+ return client != null;
}
/**
@@ -325,35 +330,14 @@
* @return partition client information
*/
public PartitionClientInfo clientInfo() {
- return new PartitionClientInfo(partition.getId(),
- partition.getMembers(),
- resourceClient.client().session().id(),
- mapper.apply(resourceClient.client().state()));
+ return new PartitionClientInfo(partition.getId(), partition.getMembers());
}
- private ResourceClient newResourceClient(Transport transport,
- io.atomix.catalyst.serializer.Serializer serializer,
- Collection<ResourceType> resourceTypes) {
- ResourceRegistry registry = new ResourceRegistry();
- resourceTypes.forEach(registry::register);
- CopycatClient copycatClient = CopycatClient.builder()
- .withServerSelectionStrategy(ServerSelectionStrategies.ANY)
- .withConnectionStrategy(ConnectionStrategies.FIBONACCI_BACKOFF)
- .withRecoveryStrategy(RecoveryStrategies.RECOVER)
- .withTransport(transport)
- .withSerializer(serializer)
+ private RaftClient newRaftClient(RaftClientProtocol protocol) {
+ return RaftClient.newBuilder()
+ .withClientId("partition-" + partition.getId())
+ .withMemberId(MemberId.from(localMemberId.id()))
+ .withProtocol(protocol)
.build();
- copycatClient.serializer().resolve(new ResourceManagerTypeResolver());
- for (ResourceType type : registry.types()) {
- try {
- type.factory()
- .newInstance()
- .createSerializableTypeResolver()
- .resolve(copycatClient.serializer().registry());
- } catch (InstantiationException | IllegalAccessException e) {
- throw new ResourceManagerException(e);
- }
- }
- return new ResourceClient(new OnosCopycatClient(copycatClient, 5, 100));
}
}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java
index 9305a31..9ae34d9 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java
@@ -15,13 +15,12 @@
*/
package org.onosproject.store.primitives.impl;
-import io.atomix.copycat.server.cluster.Member;
-
import java.util.Collection;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
+import io.atomix.protocols.raft.cluster.RaftMember;
import org.onosproject.cluster.PartitionId;
import org.onosproject.store.service.PartitionInfo;
@@ -34,15 +33,15 @@
public class StoragePartitionDetails {
private final PartitionId partitionId;
- private final Set<Member> activeMembers;
- private final Set<Member> configuredMembers;
- private final Member leader;
+ private final Set<RaftMember> activeMembers;
+ private final Set<RaftMember> configuredMembers;
+ private final RaftMember leader;
private final long leaderTerm;
public StoragePartitionDetails(PartitionId partitionId,
- Collection<Member> activeMembers,
- Collection<Member> configuredMembers,
- Member leader,
+ Collection<RaftMember> activeMembers,
+ Collection<RaftMember> configuredMembers,
+ RaftMember leader,
long leaderTerm) {
this.partitionId = partitionId;
this.activeMembers = ImmutableSet.copyOf(activeMembers);
@@ -55,7 +54,7 @@
* Returns the set of active members.
* @return active members
*/
- public Set<Member> activeMembers() {
+ public Set<RaftMember> activeMembers() {
return activeMembers;
}
@@ -63,7 +62,7 @@
* Returns the set of configured members.
* @return configured members
*/
- public Set<Member> configuredMembers() {
+ public Set<RaftMember> configuredMembers() {
return configuredMembers;
}
@@ -71,7 +70,7 @@
* Returns the partition leader.
* @return leader
*/
- public Member leader() {
+ public RaftMember leader() {
return leader;
}
@@ -98,8 +97,8 @@
* @return partition info
*/
public PartitionInfo toPartitionInfo() {
- Function<Member, String> memberToString =
- m -> m == null ? "none" : String.format("%s:%d", m.address().host(), m.address().port());
+ Function<RaftMember, String> memberToString =
+ m -> m == null ? "none" : m.memberId().toString();
return new PartitionInfo(partitionId.toString(),
leaderTerm,
activeMembers.stream().map(memberToString).collect(Collectors.toList()),
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java
index f0c0609..c1ea37c 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java
@@ -15,24 +15,23 @@
*/
package org.onosproject.store.primitives.impl;
-import static org.slf4j.LoggerFactory.getLogger;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.Transport;
-import io.atomix.copycat.server.CopycatServer;
-import io.atomix.copycat.server.storage.Storage;
-import io.atomix.copycat.server.storage.StorageLevel;
-import io.atomix.manager.internal.ResourceManagerState;
-import io.atomix.manager.util.ResourceManagerTypeResolver;
-
import java.io.File;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
+import io.atomix.protocols.raft.RaftServer;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.protocol.RaftServerProtocol;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.storage.StorageLevel;
+import org.onosproject.store.primitives.resources.impl.AtomixSerializerAdapter;
import org.onosproject.store.service.PartitionInfo;
+import org.onosproject.store.service.Serializer;
import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
/**
* {@link StoragePartition} server.
*/
@@ -41,36 +40,34 @@
private final Logger log = getLogger(getClass());
private static final int MAX_ENTRIES_PER_LOG_SEGMENT = 32768;
+ private final MemberId localMemberId;
private final StoragePartition partition;
- private final Address localAddress;
- private final Supplier<Transport> transport;
- private final Serializer serializer;
+ private final Supplier<RaftServerProtocol> protocol;
private final File dataFolder;
- private CopycatServer server;
+ private RaftServer server;
- public StoragePartitionServer(Address localAddress,
+ public StoragePartitionServer(
StoragePartition partition,
- Serializer serializer,
- Supplier<Transport> transport,
+ MemberId localMemberId,
+ Supplier<RaftServerProtocol> protocol,
File dataFolder) {
this.partition = partition;
- this.localAddress = localAddress;
- this.serializer = serializer;
- this.transport = transport;
+ this.localMemberId = localMemberId;
+ this.protocol = protocol;
this.dataFolder = dataFolder;
}
@Override
public CompletableFuture<Void> open() {
- CompletableFuture<CopycatServer> serverOpenFuture;
- if (partition.getMemberAddresses().contains(localAddress)) {
+ CompletableFuture<RaftServer> serverOpenFuture;
+ if (partition.getMemberIds().contains(localMemberId)) {
if (server != null && server.isRunning()) {
return CompletableFuture.completedFuture(null);
}
synchronized (this) {
server = buildServer();
}
- serverOpenFuture = server.bootstrap(partition.getMemberAddresses());
+ serverOpenFuture = server.bootstrap(partition.getMemberIds());
} else {
serverOpenFuture = CompletableFuture.completedFuture(null);
}
@@ -96,24 +93,21 @@
return server.leave();
}
- private CopycatServer buildServer() {
- CopycatServer server = CopycatServer.builder(localAddress)
+ private RaftServer buildServer() {
+ RaftServer.Builder builder = RaftServer.newBuilder(localMemberId)
.withName("partition-" + partition.getId())
- .withSerializer(serializer.clone())
- .withTransport(transport.get())
- .withStateMachine(ResourceManagerState::new)
- .withStorage(Storage.builder()
+ .withProtocol(protocol.get())
+ .withStorage(RaftStorage.newBuilder()
.withStorageLevel(StorageLevel.DISK)
- .withCompactionThreads(1)
+ .withSerializer(new AtomixSerializerAdapter(Serializer.using(StorageNamespaces.RAFT_STORAGE)))
.withDirectory(dataFolder)
.withMaxEntriesPerSegment(MAX_ENTRIES_PER_LOG_SEGMENT)
- .build())
- .build();
- server.serializer().resolve(new ResourceManagerTypeResolver());
- return server;
+ .build());
+ StoragePartition.RAFT_SERVICES.forEach(builder::addService);
+ return builder.build();
}
- public CompletableFuture<Void> join(Collection<Address> otherMembers) {
+ public CompletableFuture<Void> join(Collection<MemberId> otherMembers) {
server = buildServer();
return server.join(otherMembers).whenComplete((r, e) -> {
if (e == null) {
@@ -135,9 +129,9 @@
*/
public PartitionInfo info() {
return new StoragePartitionDetails(partition.getId(),
- server.cluster().members(),
- server.cluster().members(),
- server.cluster().leader(),
- server.cluster().term()).toPartitionInfo();
+ server.cluster().getMembers(),
+ server.cluster().getMembers(),
+ server.cluster().getLeader(),
+ server.cluster().getTerm()).toPartitionInfo();
}
}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java
new file mode 100644
index 0000000..d023788
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onosproject.store.service.DistributedPrimitive;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstract base class for primitives that interact with Raft replicated state machines via proxy.
+ */
+public abstract class AbstractRaftPrimitive implements DistributedPrimitive {
+ private final Function<RaftProxy.State, Status> mapper = state -> {
+ switch (state) {
+ case CONNECTED:
+ return Status.ACTIVE;
+ case SUSPENDED:
+ return Status.SUSPENDED;
+ case CLOSED:
+ return Status.INACTIVE;
+ default:
+ throw new IllegalStateException("Unknown state " + state);
+ }
+ };
+
+ protected final RaftProxy proxy;
+ private final Set<Consumer<Status>> statusChangeListeners = Sets.newCopyOnWriteArraySet();
+
+ public AbstractRaftPrimitive(RaftProxy proxy) {
+ this.proxy = checkNotNull(proxy, "proxy cannot be null");
+ proxy.addStateChangeListener(this::onStateChange);
+ }
+
+ @Override
+ public String name() {
+ return proxy.name();
+ }
+
+ /**
+ * Handles a Raft session state change.
+ *
+ * @param state the updated Raft session state
+ */
+ private void onStateChange(RaftProxy.State state) {
+ statusChangeListeners.forEach(listener -> listener.accept(mapper.apply(state)));
+ }
+
+ @Override
+ public void addStatusChangeListener(Consumer<Status> listener) {
+ statusChangeListeners.add(listener);
+ }
+
+ @Override
+ public void removeStatusChangeListener(Consumer<Status> listener) {
+ statusChangeListeners.remove(listener);
+ }
+
+ @Override
+ public Collection<Consumer<Status>> statusChangeListeners() {
+ return ImmutableSet.copyOf(statusChangeListeners);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("proxy", proxy)
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java
index ad1de2e..c84e03a 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java
@@ -15,117 +15,131 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.AddAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.DecrementAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Get;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndAdd;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndDecrement;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndIncrement;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IncrementAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IsEmpty;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Put;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.PutIfAbsent;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Remove;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.RemoveValue;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Replace;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Size;
-import org.onosproject.store.service.AsyncAtomicCounterMap;
-
-import java.util.Properties;
import java.util.concurrent.CompletableFuture;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.AddAndGet;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DecrementAndGet;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Get;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndAdd;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndDecrement;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndIncrement;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IncrementAndGet;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Put;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PutIfAbsent;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Remove;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.RemoveValue;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Replace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.AsyncAtomicCounterMap;
+import org.onosproject.store.service.Serializer;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.ADD_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DECREMENT_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_DECREMENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_INCREMENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.INCREMENT_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT_IF_ABSENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REPLACE;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.SIZE;
+
/**
* {@code AsyncAtomicCounterMap} implementation backed by Atomix.
*/
-@ResourceTypeInfo(id = -157, factory = AtomixAtomicCounterMapFactory.class)
-public class AtomixAtomicCounterMap extends AbstractResource<AtomixAtomicCounterMap>
- implements AsyncAtomicCounterMap<String> {
+public class AtomixAtomicCounterMap extends AbstractRaftPrimitive implements AsyncAtomicCounterMap<String> {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixAtomicCounterMapOperations.NAMESPACE)
+ .build());
- public AtomixAtomicCounterMap(CopycatClient client, Properties options) {
- super(client, options);
- }
-
- @Override
- public String name() {
- return null;
+ public AtomixAtomicCounterMap(RaftProxy proxy) {
+ super(proxy);
}
@Override
public CompletableFuture<Long> incrementAndGet(String key) {
- return client.submit(new IncrementAndGet(key));
+ return proxy.invoke(INCREMENT_AND_GET, SERIALIZER::encode, new IncrementAndGet(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> decrementAndGet(String key) {
- return client.submit(new DecrementAndGet(key));
+ return proxy.invoke(DECREMENT_AND_GET, SERIALIZER::encode, new DecrementAndGet(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> getAndIncrement(String key) {
- return client.submit(new GetAndIncrement(key));
+ return proxy.invoke(GET_AND_INCREMENT, SERIALIZER::encode, new GetAndIncrement(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> getAndDecrement(String key) {
- return client.submit(new GetAndDecrement(key));
+ return proxy.invoke(GET_AND_DECREMENT, SERIALIZER::encode, new GetAndDecrement(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> addAndGet(String key, long delta) {
- return client.submit(new AddAndGet(key, delta));
+ return proxy.invoke(ADD_AND_GET, SERIALIZER::encode, new AddAndGet(key, delta), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> getAndAdd(String key, long delta) {
- return client.submit(new GetAndAdd(key, delta));
+ return proxy.invoke(GET_AND_ADD, SERIALIZER::encode, new GetAndAdd(key, delta), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> get(String key) {
- return client.submit(new Get(key));
+ return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> put(String key, long newValue) {
- return client.submit(new Put(key, newValue));
+ return proxy.invoke(PUT, SERIALIZER::encode, new Put(key, newValue), SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> putIfAbsent(String key, long newValue) {
- return client.submit(new PutIfAbsent(key, newValue));
+ return proxy.invoke(PUT_IF_ABSENT, SERIALIZER::encode, new PutIfAbsent(key, newValue), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> replace(String key, long expectedOldValue, long newValue) {
- return client.submit(new Replace(key, expectedOldValue, newValue));
+ return proxy.invoke(
+ REPLACE,
+ SERIALIZER::encode,
+ new Replace(key, expectedOldValue, newValue),
+ SERIALIZER::decode);
}
@Override
public CompletableFuture<Long> remove(String key) {
- return client.submit(new Remove(key));
+ return proxy.invoke(REMOVE, SERIALIZER::encode, new Remove(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> remove(String key, long value) {
- return client.submit(new RemoveValue(key, value));
+ return proxy.invoke(REMOVE_VALUE, SERIALIZER::encode, new RemoveValue(key, value), SERIALIZER::decode);
}
@Override
public CompletableFuture<Integer> size() {
- return client.submit(new Size());
+ return proxy.invoke(SIZE, SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> isEmpty() {
- return client.submit(new IsEmpty());
+ return proxy.invoke(IS_EMPTY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Void> clear() {
- return client.submit(new Clear());
+ return proxy.invoke(CLEAR);
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java
deleted file mode 100644
index 3a7f696..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-
-/**
- * Atomic counter map commands.
- */
-public final class AtomixAtomicCounterMapCommands {
- private AtomixAtomicCounterMapCommands() {
- }
-
- public abstract static class AtomicCounterMapCommand<V> implements Command<V>, CatalystSerializable {
- @Override
- public CompactionMode compaction() {
- return CompactionMode.SNAPSHOT;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- public abstract static class AtomicCounterMapQuery<V> implements Query<V>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- public abstract static class KeyCommand<V> extends AtomicCounterMapCommand<V> {
- private String key;
-
- public KeyCommand() {
- }
-
- public KeyCommand(String key) {
- this.key = key;
- }
-
- public String key() {
- return key;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeString(key);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- key = buffer.readString();
- }
- }
-
- public abstract static class KeyQuery<V> extends AtomicCounterMapQuery<V> {
- private String key;
-
- public KeyQuery() {
- }
-
- public KeyQuery(String key) {
- this.key = key;
- }
-
- public String key() {
- return key;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeString(key);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- key = buffer.readString();
- }
- }
-
- public static class KeyValueCommand<V> extends KeyCommand<V> {
- private long value;
-
- public KeyValueCommand() {
- }
-
- public KeyValueCommand(String key, long value) {
- super(key);
- this.value = value;
- }
-
- public long value() {
- return value;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- buffer.writeLong(value);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- value = buffer.readLong();
- }
- }
-
- public static class Get extends KeyQuery<Long> {
- public Get() {
- }
-
- public Get(String key) {
- super(key);
- }
- }
-
- public static class Put extends KeyValueCommand<Long> {
- public Put() {
- }
-
- public Put(String key, long value) {
- super(key, value);
- }
- }
-
- public static class PutIfAbsent extends KeyValueCommand<Long> {
- public PutIfAbsent() {
- }
-
- public PutIfAbsent(String key, long value) {
- super(key, value);
- }
- }
-
- public static class Replace extends KeyCommand<Boolean> {
- private long replace;
- private long value;
-
- public Replace() {
- }
-
- public Replace(String key, long replace, long value) {
- super(key);
- this.replace = replace;
- this.value = value;
- }
-
- public long replace() {
- return replace;
- }
-
- public long value() {
- return value;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- buffer.writeLong(replace);
- buffer.writeLong(value);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- replace = buffer.readLong();
- value = buffer.readLong();
- }
- }
-
- public static class Remove extends KeyCommand<Long> {
- public Remove() {
- }
-
- public Remove(String key) {
- super(key);
- }
- }
-
- public static class RemoveValue extends KeyValueCommand<Boolean> {
- public RemoveValue() {
- }
-
- public RemoveValue(String key, long value) {
- super(key, value);
- }
- }
-
- public static class IncrementAndGet extends KeyCommand<Long> {
- public IncrementAndGet() {
- }
-
- public IncrementAndGet(String key) {
- super(key);
- }
- }
-
- public static class DecrementAndGet extends KeyCommand<Long> {
- public DecrementAndGet(String key) {
- super(key);
- }
-
- public DecrementAndGet() {
- }
- }
-
- public static class GetAndIncrement extends KeyCommand<Long> {
- public GetAndIncrement() {
- }
-
- public GetAndIncrement(String key) {
- super(key);
- }
- }
-
- public static class GetAndDecrement extends KeyCommand<Long> {
- public GetAndDecrement() {
- }
-
- public GetAndDecrement(String key) {
- super(key);
- }
- }
-
- public abstract static class DeltaCommand extends KeyCommand<Long> {
- private long delta;
-
- public DeltaCommand() {
- }
-
- public DeltaCommand(String key, long delta) {
- super(key);
- this.delta = delta;
- }
-
- public long delta() {
- return delta;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- buffer.writeLong(delta);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- delta = buffer.readLong();
- }
- }
-
- public static class AddAndGet extends DeltaCommand {
- public AddAndGet() {
- }
-
- public AddAndGet(String key, long delta) {
- super(key, delta);
- }
- }
-
- public static class GetAndAdd extends DeltaCommand {
- public GetAndAdd() {
- }
-
- public GetAndAdd(String key, long delta) {
- super(key, delta);
- }
- }
-
- public static class Size extends AtomicCounterMapQuery<Integer> {
- }
-
- public static class IsEmpty extends AtomicCounterMapQuery<Boolean> {
- }
-
- public static class Clear extends AtomicCounterMapCommand<Void> {
- }
-
- /**
- * Counter map command type resolver.
- */
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- registry.register(Get.class, -790);
- registry.register(Put.class, -791);
- registry.register(PutIfAbsent.class, -792);
- registry.register(Replace.class, -793);
- registry.register(Remove.class, -794);
- registry.register(RemoveValue.class, -795);
- registry.register(IncrementAndGet.class, -796);
- registry.register(DecrementAndGet.class, -797);
- registry.register(GetAndIncrement.class, -798);
- registry.register(GetAndDecrement.class, -799);
- registry.register(AddAndGet.class, -800);
- registry.register(GetAndAdd.class, -801);
- registry.register(Size.class, -801);
- registry.register(IsEmpty.class, -801);
- registry.register(Clear.class, -801);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java
deleted file mode 100644
index 4caf68e..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * Atomic counter map factory.
- */
-public class AtomixAtomicCounterMapFactory implements ResourceFactory<AtomixAtomicCounterMap> {
-
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixAtomicCounterMapCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixAtomicCounterMapState(config);
- }
-
- @Override
- public AtomixAtomicCounterMap createInstance(CopycatClient client, Properties options) {
- return new AtomixAtomicCounterMap(client, options);
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java
new file mode 100644
index 0000000..df3508f
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Atomic counter map commands.
+ */
+public enum AtomixAtomicCounterMapOperations implements OperationId {
+ PUT("put", OperationType.COMMAND),
+ PUT_IF_ABSENT("putIfAbsent", OperationType.COMMAND),
+ GET("get", OperationType.QUERY),
+ REPLACE("replace", OperationType.COMMAND),
+ REMOVE("remove", OperationType.COMMAND),
+ REMOVE_VALUE("removeValue", OperationType.COMMAND),
+ GET_AND_INCREMENT("getAndIncrement", OperationType.COMMAND),
+ GET_AND_DECREMENT("getAndDecrement", OperationType.COMMAND),
+ INCREMENT_AND_GET("incrementAndGet", OperationType.COMMAND),
+ DECREMENT_AND_GET("decrementAndGet", OperationType.COMMAND),
+ ADD_AND_GET("addAndGet", OperationType.COMMAND),
+ GET_AND_ADD("getAndAdd", OperationType.COMMAND),
+ SIZE("size", OperationType.QUERY),
+ IS_EMPTY("isEmpty", OperationType.QUERY),
+ CLEAR("clear", OperationType.COMMAND);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixAtomicCounterMapOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(IncrementAndGet.class)
+ .register(DecrementAndGet.class)
+ .register(GetAndIncrement.class)
+ .register(GetAndDecrement.class)
+ .register(AddAndGet.class)
+ .register(GetAndAdd.class)
+ .register(Get.class)
+ .register(Put.class)
+ .register(PutIfAbsent.class)
+ .register(Replace.class)
+ .register(Remove.class)
+ .register(RemoveValue.class)
+ .build("AtomixAtomicCounterMapOperations");
+
+ public abstract static class AtomicCounterMapOperation<V> {
+ }
+
+ public abstract static class KeyOperation extends AtomicCounterMapOperation {
+ private String key;
+
+ public KeyOperation() {
+ }
+
+ public KeyOperation(String key) {
+ this.key = key;
+ }
+
+ public String key() {
+ return key;
+ }
+ }
+
+ public static class KeyValueOperation extends KeyOperation {
+ private long value;
+
+ public KeyValueOperation() {
+ }
+
+ public KeyValueOperation(String key, long value) {
+ super(key);
+ this.value = value;
+ }
+
+ public long value() {
+ return value;
+ }
+ }
+
+ public static class Get extends KeyOperation {
+ public Get() {
+ }
+
+ public Get(String key) {
+ super(key);
+ }
+ }
+
+ public static class Put extends KeyValueOperation {
+ public Put() {
+ }
+
+ public Put(String key, long value) {
+ super(key, value);
+ }
+ }
+
+ public static class PutIfAbsent extends KeyValueOperation {
+ public PutIfAbsent() {
+ }
+
+ public PutIfAbsent(String key, long value) {
+ super(key, value);
+ }
+ }
+
+ public static class Replace extends KeyOperation {
+ private long replace;
+ private long value;
+
+ public Replace() {
+ }
+
+ public Replace(String key, long replace, long value) {
+ super(key);
+ this.replace = replace;
+ this.value = value;
+ }
+
+ public long replace() {
+ return replace;
+ }
+
+ public long value() {
+ return value;
+ }
+ }
+
+ public static class Remove extends KeyOperation {
+ public Remove() {
+ }
+
+ public Remove(String key) {
+ super(key);
+ }
+ }
+
+ public static class RemoveValue extends KeyValueOperation {
+ public RemoveValue() {
+ }
+
+ public RemoveValue(String key, long value) {
+ super(key, value);
+ }
+ }
+
+ public static class IncrementAndGet extends KeyOperation {
+ public IncrementAndGet() {
+ }
+
+ public IncrementAndGet(String key) {
+ super(key);
+ }
+ }
+
+ public static class DecrementAndGet extends KeyOperation {
+ public DecrementAndGet(String key) {
+ super(key);
+ }
+
+ public DecrementAndGet() {
+ }
+ }
+
+ public static class GetAndIncrement extends KeyOperation {
+ public GetAndIncrement() {
+ }
+
+ public GetAndIncrement(String key) {
+ super(key);
+ }
+ }
+
+ public static class GetAndDecrement extends KeyOperation {
+ public GetAndDecrement() {
+ }
+
+ public GetAndDecrement(String key) {
+ super(key);
+ }
+ }
+
+ public abstract static class DeltaOperation extends KeyOperation {
+ private long delta;
+
+ public DeltaOperation() {
+ }
+
+ public DeltaOperation(String key, long delta) {
+ super(key);
+ this.delta = delta;
+ }
+
+ public long delta() {
+ return delta;
+ }
+ }
+
+ public static class AddAndGet extends DeltaOperation {
+ public AddAndGet() {
+ }
+
+ public AddAndGet(String key, long delta) {
+ super(key, delta);
+ }
+ }
+
+ public static class GetAndAdd extends DeltaOperation {
+ public GetAndAdd() {
+ }
+
+ public GetAndAdd(String key, long delta) {
+ super(key, delta);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java
new file mode 100644
index 0000000..b895d71
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.AddAndGet;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DecrementAndGet;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Get;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndAdd;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndDecrement;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndIncrement;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IncrementAndGet;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Put;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PutIfAbsent;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Remove;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.RemoveValue;
+import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Replace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.ADD_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DECREMENT_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_DECREMENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_INCREMENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.INCREMENT_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT_IF_ABSENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REPLACE;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.SIZE;
+
+/**
+ * Atomic counter map state for Atomix.
+ * <p>
+ * The counter map state is implemented as a snapshottable state machine. Snapshots are necessary
+ * since incremental compaction is impractical for counters where the value of a counter is the sum
+ * of all its increments. Note that this snapshotting large state machines may risk blocking of the
+ * Raft cluster with the current implementation of snapshotting in Copycat.
+ */
+public class AtomixAtomicCounterMapService extends AbstractRaftService {
+
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixAtomicCounterMapOperations.NAMESPACE)
+ .build());
+
+ private Map<String, Long> map = new HashMap<>();
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
+ executor.register(PUT, SERIALIZER::decode, this::put, SERIALIZER::encode);
+ executor.register(PUT_IF_ABSENT, SERIALIZER::decode, this::putIfAbsent, SERIALIZER::encode);
+ executor.register(GET, SERIALIZER::decode, this::get, SERIALIZER::encode);
+ executor.register(REPLACE, SERIALIZER::decode, this::replace, SERIALIZER::encode);
+ executor.register(REMOVE, SERIALIZER::decode, this::remove, SERIALIZER::encode);
+ executor.register(REMOVE_VALUE, SERIALIZER::decode, this::removeValue, SERIALIZER::encode);
+ executor.register(GET_AND_INCREMENT, SERIALIZER::decode, this::getAndIncrement, SERIALIZER::encode);
+ executor.register(GET_AND_DECREMENT, SERIALIZER::decode, this::getAndDecrement, SERIALIZER::encode);
+ executor.register(INCREMENT_AND_GET, SERIALIZER::decode, this::incrementAndGet, SERIALIZER::encode);
+ executor.register(DECREMENT_AND_GET, SERIALIZER::decode, this::decrementAndGet, SERIALIZER::encode);
+ executor.register(ADD_AND_GET, SERIALIZER::decode, this::addAndGet, SERIALIZER::encode);
+ executor.register(GET_AND_ADD, SERIALIZER::decode, this::getAndAdd, SERIALIZER::encode);
+ executor.register(SIZE, this::size, SERIALIZER::encode);
+ executor.register(IS_EMPTY, this::isEmpty, SERIALIZER::encode);
+ executor.register(CLEAR, this::clear);
+ }
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeObject(map, SERIALIZER::encode);
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ map = reader.readObject(SERIALIZER::decode);
+ }
+
+ /**
+ * Returns the primitive value for the given primitive wrapper.
+ */
+ private long primitive(Long value) {
+ if (value != null) {
+ return value;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Handles a {@link Put} command which implements {@link AtomixAtomicCounterMap#put(String, long)}.
+ *
+ * @param commit put commit
+ * @return put result
+ */
+ protected long put(Commit<Put> commit) {
+ return primitive(map.put(commit.value().key(), commit.value().value()));
+ }
+
+ /**
+ * Handles a {@link PutIfAbsent} command which implements {@link AtomixAtomicCounterMap#putIfAbsent(String, long)}.
+ *
+ * @param commit putIfAbsent commit
+ * @return putIfAbsent result
+ */
+ protected long putIfAbsent(Commit<PutIfAbsent> commit) {
+ return primitive(map.putIfAbsent(commit.value().key(), commit.value().value()));
+ }
+
+ /**
+ * Handles a {@link Get} query which implements {@link AtomixAtomicCounterMap#get(String)}}.
+ *
+ * @param commit get commit
+ * @return get result
+ */
+ protected long get(Commit<Get> commit) {
+ return primitive(map.get(commit.value().key()));
+ }
+
+ /**
+ * Handles a {@link Replace} command which implements {@link AtomixAtomicCounterMap#replace(String, long, long)}.
+ *
+ * @param commit replace commit
+ * @return replace result
+ */
+ protected boolean replace(Commit<Replace> commit) {
+ Long value = map.get(commit.value().key());
+ if (value == null) {
+ if (commit.value().replace() == 0) {
+ map.put(commit.value().key(), commit.value().value());
+ return true;
+ } else {
+ return false;
+ }
+ } else if (value == commit.value().replace()) {
+ map.put(commit.value().key(), commit.value().value());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handles a {@link Remove} command which implements {@link AtomixAtomicCounterMap#remove(String)}.
+ *
+ * @param commit remove commit
+ * @return remove result
+ */
+ protected long remove(Commit<Remove> commit) {
+ return primitive(map.remove(commit.value().key()));
+ }
+
+ /**
+ * Handles a {@link RemoveValue} command which implements {@link AtomixAtomicCounterMap#remove(String, long)}.
+ *
+ * @param commit removeValue commit
+ * @return removeValue result
+ */
+ protected boolean removeValue(Commit<RemoveValue> commit) {
+ Long value = map.get(commit.value().key());
+ if (value == null) {
+ if (commit.value().value() == 0) {
+ map.remove(commit.value().key());
+ return true;
+ }
+ return false;
+ } else if (value == commit.value().value()) {
+ map.remove(commit.value().key());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handles a {@link GetAndIncrement} command which implements
+ * {@link AtomixAtomicCounterMap#getAndIncrement(String)}.
+ *
+ * @param commit getAndIncrement commit
+ * @return getAndIncrement result
+ */
+ protected long getAndIncrement(Commit<GetAndIncrement> commit) {
+ long value = primitive(map.get(commit.value().key()));
+ map.put(commit.value().key(), value + 1);
+ return value;
+ }
+
+ /**
+ * Handles a {@link GetAndDecrement} command which implements
+ * {@link AtomixAtomicCounterMap#getAndDecrement(String)}.
+ *
+ * @param commit getAndDecrement commit
+ * @return getAndDecrement result
+ */
+ protected long getAndDecrement(Commit<GetAndDecrement> commit) {
+ long value = primitive(map.get(commit.value().key()));
+ map.put(commit.value().key(), value - 1);
+ return value;
+ }
+
+ /**
+ * Handles a {@link IncrementAndGet} command which implements
+ * {@link AtomixAtomicCounterMap#incrementAndGet(String)}.
+ *
+ * @param commit incrementAndGet commit
+ * @return incrementAndGet result
+ */
+ protected long incrementAndGet(Commit<IncrementAndGet> commit) {
+ long value = primitive(map.get(commit.value().key()));
+ map.put(commit.value().key(), ++value);
+ return value;
+ }
+
+ /**
+ * Handles a {@link DecrementAndGet} command which implements
+ * {@link AtomixAtomicCounterMap#decrementAndGet(String)}.
+ *
+ * @param commit decrementAndGet commit
+ * @return decrementAndGet result
+ */
+ protected long decrementAndGet(Commit<DecrementAndGet> commit) {
+ long value = primitive(map.get(commit.value().key()));
+ map.put(commit.value().key(), --value);
+ return value;
+ }
+
+ /**
+ * Handles a {@link AddAndGet} command which implements {@link AtomixAtomicCounterMap#addAndGet(String, long)}.
+ *
+ * @param commit addAndGet commit
+ * @return addAndGet result
+ */
+ protected long addAndGet(Commit<AddAndGet> commit) {
+ long value = primitive(map.get(commit.value().key()));
+ value += commit.value().delta();
+ map.put(commit.value().key(), value);
+ return value;
+ }
+
+ /**
+ * Handles a {@link GetAndAdd} command which implements {@link AtomixAtomicCounterMap#getAndAdd(String, long)}.
+ *
+ * @param commit getAndAdd commit
+ * @return getAndAdd result
+ */
+ protected long getAndAdd(Commit<GetAndAdd> commit) {
+ long value = primitive(map.get(commit.value().key()));
+ map.put(commit.value().key(), value + commit.value().delta());
+ return value;
+ }
+
+ /**
+ * Handles a {@code Size} query which implements {@link AtomixAtomicCounterMap#size()}.
+ *
+ * @param commit size commit
+ * @return size result
+ */
+ protected int size(Commit<Void> commit) {
+ return map.size();
+ }
+
+ /**
+ * Handles an {@code IsEmpty} query which implements {@link AtomixAtomicCounterMap#isEmpty()}.
+ *
+ * @param commit isEmpty commit
+ * @return isEmpty result
+ */
+ protected boolean isEmpty(Commit<Void> commit) {
+ return map.isEmpty();
+ }
+
+ /**
+ * Handles a {@code Clear} command which implements {@link AtomixAtomicCounterMap#clear()}.
+ *
+ * @param commit clear commit
+ */
+ protected void clear(Commit<Void> commit) {
+ map.clear();
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java
deleted file mode 100644
index 1aed4a8..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.Snapshottable;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.storage.snapshot.SnapshotReader;
-import io.atomix.copycat.server.storage.snapshot.SnapshotWriter;
-import io.atomix.resource.ResourceStateMachine;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.AddAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.DecrementAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Get;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndAdd;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndDecrement;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndIncrement;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IncrementAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IsEmpty;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Put;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.PutIfAbsent;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Remove;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.RemoveValue;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Replace;
-import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Size;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Atomic counter map state for Atomix.
- * <p>
- * The counter map state is implemented as a snapshottable state machine. Snapshots are necessary
- * since incremental compaction is impractical for counters where the value of a counter is the sum
- * of all its increments. Note that this snapshotting large state machines may risk blocking of the
- * Raft cluster with the current implementation of snapshotting in Copycat.
- */
-public class AtomixAtomicCounterMapState extends ResourceStateMachine implements Snapshottable {
- private Map<String, Long> map = new HashMap<>();
-
- public AtomixAtomicCounterMapState(Properties config) {
- super(config);
- }
-
- @Override
- protected void configure(StateMachineExecutor executor) {
- executor.register(Put.class, this::put);
- executor.register(PutIfAbsent.class, this::putIfAbsent);
- executor.register(Get.class, this::get);
- executor.register(Replace.class, this::replace);
- executor.register(Remove.class, this::remove);
- executor.register(RemoveValue.class, this::removeValue);
- executor.register(GetAndIncrement.class, this::getAndIncrement);
- executor.register(GetAndDecrement.class, this::getAndDecrement);
- executor.register(IncrementAndGet.class, this::incrementAndGet);
- executor.register(DecrementAndGet.class, this::decrementAndGet);
- executor.register(AddAndGet.class, this::addAndGet);
- executor.register(GetAndAdd.class, this::getAndAdd);
- executor.register(Size.class, this::size);
- executor.register(IsEmpty.class, this::isEmpty);
- executor.register(Clear.class, this::clear);
- }
-
- @Override
- public void snapshot(SnapshotWriter writer) {
- writer.writeObject(map);
- }
-
- @Override
- public void install(SnapshotReader reader) {
- map = reader.readObject();
- }
-
- /**
- * Returns the primitive value for the given primitive wrapper.
- */
- private long primitive(Long value) {
- if (value != null) {
- return value;
- } else {
- return 0;
- }
- }
-
- /**
- * Handles a {@link Put} command which implements {@link AtomixAtomicCounterMap#put(String, long)}.
- *
- * @param commit put commit
- * @return put result
- */
- protected long put(Commit<Put> commit) {
- try {
- return primitive(map.put(commit.operation().key(), commit.operation().value()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link PutIfAbsent} command which implements {@link AtomixAtomicCounterMap#putIfAbsent(String, long)}.
- *
- * @param commit putIfAbsent commit
- * @return putIfAbsent result
- */
- protected long putIfAbsent(Commit<PutIfAbsent> commit) {
- try {
- return primitive(map.putIfAbsent(commit.operation().key(), commit.operation().value()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link Get} query which implements {@link AtomixAtomicCounterMap#get(String)}}.
- *
- * @param commit get commit
- * @return get result
- */
- protected long get(Commit<Get> commit) {
- try {
- return primitive(map.get(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link Replace} command which implements {@link AtomixAtomicCounterMap#replace(String, long, long)}.
- *
- * @param commit replace commit
- * @return replace result
- */
- protected boolean replace(Commit<Replace> commit) {
- try {
- Long value = map.get(commit.operation().key());
- if (value == null) {
- if (commit.operation().replace() == 0) {
- map.put(commit.operation().key(), commit.operation().value());
- return true;
- } else {
- return false;
- }
- } else if (value == commit.operation().replace()) {
- map.put(commit.operation().key(), commit.operation().value());
- return true;
- }
- return false;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link Remove} command which implements {@link AtomixAtomicCounterMap#remove(String)}.
- *
- * @param commit remove commit
- * @return remove result
- */
- protected long remove(Commit<Remove> commit) {
- try {
- return primitive(map.remove(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link RemoveValue} command which implements {@link AtomixAtomicCounterMap#remove(String, long)}.
- *
- * @param commit removeValue commit
- * @return removeValue result
- */
- protected boolean removeValue(Commit<RemoveValue> commit) {
- try {
- Long value = map.get(commit.operation().key());
- if (value == null) {
- if (commit.operation().value() == 0) {
- map.remove(commit.operation().key());
- return true;
- }
- return false;
- } else if (value == commit.operation().value()) {
- map.remove(commit.operation().key());
- return true;
- }
- return false;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link GetAndIncrement} command which implements
- * {@link AtomixAtomicCounterMap#getAndIncrement(String)}.
- *
- * @param commit getAndIncrement commit
- * @return getAndIncrement result
- */
- protected long getAndIncrement(Commit<GetAndIncrement> commit) {
- try {
- long value = primitive(map.get(commit.operation().key()));
- map.put(commit.operation().key(), value + 1);
- return value;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link GetAndDecrement} command which implements
- * {@link AtomixAtomicCounterMap#getAndDecrement(String)}.
- *
- * @param commit getAndDecrement commit
- * @return getAndDecrement result
- */
- protected long getAndDecrement(Commit<GetAndDecrement> commit) {
- try {
- long value = primitive(map.get(commit.operation().key()));
- map.put(commit.operation().key(), value - 1);
- return value;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link IncrementAndGet} command which implements
- * {@link AtomixAtomicCounterMap#incrementAndGet(String)}.
- *
- * @param commit incrementAndGet commit
- * @return incrementAndGet result
- */
- protected long incrementAndGet(Commit<IncrementAndGet> commit) {
- try {
- long value = primitive(map.get(commit.operation().key()));
- map.put(commit.operation().key(), ++value);
- return value;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link DecrementAndGet} command which implements
- * {@link AtomixAtomicCounterMap#decrementAndGet(String)}.
- *
- * @param commit decrementAndGet commit
- * @return decrementAndGet result
- */
- protected long decrementAndGet(Commit<DecrementAndGet> commit) {
- try {
- long value = primitive(map.get(commit.operation().key()));
- map.put(commit.operation().key(), --value);
- return value;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link AddAndGet} command which implements {@link AtomixAtomicCounterMap#addAndGet(String, long)}.
- *
- * @param commit addAndGet commit
- * @return addAndGet result
- */
- protected long addAndGet(Commit<AddAndGet> commit) {
- try {
- long value = primitive(map.get(commit.operation().key()));
- value += commit.operation().delta();
- map.put(commit.operation().key(), value);
- return value;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link GetAndAdd} command which implements {@link AtomixAtomicCounterMap#getAndAdd(String, long)}.
- *
- * @param commit getAndAdd commit
- * @return getAndAdd result
- */
- protected long getAndAdd(Commit<GetAndAdd> commit) {
- try {
- long value = primitive(map.get(commit.operation().key()));
- map.put(commit.operation().key(), value + commit.operation().delta());
- return value;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link Size} query which implements {@link AtomixAtomicCounterMap#size()}.
- *
- * @param commit size commit
- * @return size result
- */
- protected int size(Commit<Size> commit) {
- try {
- return map.size();
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles an {@link IsEmpty} query which implements {@link AtomixAtomicCounterMap#isEmpty()}.
- *
- * @param commit isEmpty commit
- * @return isEmpty result
- */
- protected boolean isEmpty(Commit<IsEmpty> commit) {
- try {
- return map.isEmpty();
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a {@link Clear} command which implements {@link AtomixAtomicCounterMap#clear()}.
- *
- * @param commit clear commit
- */
- protected void clear(Commit<Clear> commit) {
- try {
- map.clear();
- } finally {
- commit.close();
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java
index 7220c37..f3a1ea2 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java
@@ -15,146 +15,149 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-
-import java.util.concurrent.ConcurrentHashMap;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
-import java.util.function.Consumer;
import java.util.function.Predicate;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
import org.onlab.util.Match;
import org.onlab.util.Tools;
import org.onosproject.store.primitives.MapUpdate;
import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsKey;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsValue;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.EntrySet;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Get;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.GetOrDefault;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.IsEmpty;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.KeySet;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Listen;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Size;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionBegin;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionCommit;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepare;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepareAndCommit;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionRollback;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Unlisten;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.UpdateAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Values;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsKey;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsValue;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.Get;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GetOrDefault;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionBegin;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionCommit;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepare;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepareAndCommit;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionRollback;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UpdateAndGet;
+import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.AsyncConsistentMap;
import org.onosproject.store.service.ConsistentMapException;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.TransactionLog;
import org.onosproject.store.service.Version;
import org.onosproject.store.service.Versioned;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.BEGIN;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.COMMIT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ENTRY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET_OR_DEFAULT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.KEY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE_AND_COMMIT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ROLLBACK;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.SIZE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UPDATE_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.VALUES;
/**
* Distributed resource providing the {@link AsyncConsistentMap} primitive.
*/
-@ResourceTypeInfo(id = -151, factory = AtomixConsistentMapFactory.class)
-public class AtomixConsistentMap extends AbstractResource<AtomixConsistentMap>
- implements AsyncConsistentMap<String, byte[]> {
+public class AtomixConsistentMap extends AbstractRaftPrimitive implements AsyncConsistentMap<String, byte[]> {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixConsistentMapOperations.NAMESPACE)
+ .register(AtomixConsistentMapEvents.NAMESPACE)
+ .build());
- private final Set<Consumer<Status>> statusChangeListeners = Sets.newCopyOnWriteArraySet();
private final Map<MapEventListener<String, byte[]>, Executor> mapEventListeners = new ConcurrentHashMap<>();
- public static final String CHANGE_SUBJECT = "changeEvents";
-
- public AtomixConsistentMap(CopycatClient client, Properties properties) {
- super(client, properties);
- }
-
- @Override
- public String name() {
- return null;
- }
-
- @Override
- public CompletableFuture<AtomixConsistentMap> open() {
- return super.open().thenApply(result -> {
- client.onStateChange(state -> {
- if (state == CopycatClient.State.CONNECTED && isListening()) {
- client.submit(new Listen());
- }
- });
- client.onEvent(CHANGE_SUBJECT, this::handleEvent);
- return result;
+ public AtomixConsistentMap(RaftProxy proxy) {
+ super(proxy);
+ proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent);
+ proxy.addStateChangeListener(state -> {
+ if (state == RaftProxy.State.CONNECTED && isListening()) {
+ proxy.invoke(ADD_LISTENER);
+ }
});
}
private void handleEvent(List<MapEvent<String, byte[]>> events) {
events.forEach(event ->
- mapEventListeners.forEach((listener, executor) -> executor.execute(() -> listener.event(event))));
+ mapEventListeners.forEach((listener, executor) -> executor.execute(() -> listener.event(event))));
}
@Override
public CompletableFuture<Boolean> isEmpty() {
- return client.submit(new IsEmpty());
+ return proxy.invoke(IS_EMPTY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Integer> size() {
- return client.submit(new Size());
+ return proxy.invoke(SIZE, SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsKey(String key) {
- return client.submit(new ContainsKey(key));
+ return proxy.invoke(CONTAINS_KEY, SERIALIZER::encode, new ContainsKey(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsValue(byte[] value) {
- return client.submit(new ContainsValue(value));
+ return proxy.invoke(CONTAINS_VALUE, SERIALIZER::encode, new ContainsValue(value), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<byte[]>> get(String key) {
- return client.submit(new Get(key));
+ return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<byte[]>> getOrDefault(String key, byte[] defaultValue) {
- return client.submit(new GetOrDefault(key, defaultValue));
+ return proxy.invoke(
+ GET_OR_DEFAULT,
+ SERIALIZER::encode,
+ new GetOrDefault(key, defaultValue),
+ SERIALIZER::decode);
}
@Override
public CompletableFuture<Set<String>> keySet() {
- return client.submit(new KeySet());
+ return proxy.invoke(KEY_SET, SERIALIZER::decode);
}
@Override
public CompletableFuture<Collection<Versioned<byte[]>>> values() {
- return client.submit(new Values());
+ return proxy.invoke(VALUES, SERIALIZER::decode);
}
@Override
public CompletableFuture<Set<Entry<String, Versioned<byte[]>>>> entrySet() {
- return client.submit(new EntrySet());
+ return proxy.invoke(ENTRY_SET, SERIALIZER::decode);
}
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> put(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.ANY, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -162,7 +165,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> putAndGet(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.ANY, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.newValue());
}
@@ -170,14 +177,23 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> putIfAbsent(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.NULL, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.NULL, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
+
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> remove(String key) {
- return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, null, Match.ANY, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -185,7 +201,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> remove(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@@ -193,7 +213,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> remove(String key, long version) {
- return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version)))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version)),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@@ -201,7 +225,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> replace(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -209,7 +237,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> replace(String key, byte[] oldValue, byte[] newValue) {
- return client.submit(new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@@ -217,14 +249,18 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> replace(String key, long oldVersion, byte[] newValue) {
- return client.submit(new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion)))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion)),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@Override
public CompletableFuture<Void> clear() {
- return client.submit(new Clear())
+ return proxy.<MapEntryUpdateResult.Status>invoke(CLEAR, SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r))
.thenApply(v -> null);
}
@@ -233,7 +269,7 @@
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> computeIf(String key,
Predicate<? super byte[]> condition,
- BiFunction<? super String, ? super byte[], ? extends byte[]> remappingFunction) {
+ BiFunction<? super String, ? super byte[], ? extends byte[]> remappingFunction) {
return get(key).thenCompose(r1 -> {
byte[] existingValue = r1 == null ? null : r1.value();
// if the condition evaluates to false, return existing value.
@@ -255,27 +291,31 @@
}
Match<byte[]> valueMatch = r1 == null ? Match.NULL : Match.ANY;
Match<Long> versionMatch = r1 == null ? Match.ANY : Match.ifValue(r1.version());
- return client.submit(new UpdateAndGet(key,
- computedValue.get(),
- valueMatch,
- versionMatch))
- .whenComplete((r, e) -> throwIfLocked(r.status()))
- .thenCompose(r -> {
- if (r.status() == MapEntryUpdateResult.Status.PRECONDITION_FAILED ||
- r.status() == MapEntryUpdateResult.Status.WRITE_LOCK) {
- return Tools.exceptionalFuture(new ConsistentMapException.ConcurrentModification());
- }
- return CompletableFuture.completedFuture(r);
- })
- .thenApply(v -> v.newValue());
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key,
+ computedValue.get(),
+ valueMatch,
+ versionMatch),
+ SERIALIZER::decode)
+ .whenComplete((r, e) -> throwIfLocked(r.status()))
+ .thenCompose(r -> {
+ if (r.status() == MapEntryUpdateResult.Status.PRECONDITION_FAILED ||
+ r.status() == MapEntryUpdateResult.Status.WRITE_LOCK) {
+ return Tools.exceptionalFuture(new ConsistentMapException.ConcurrentModification());
+ }
+ return CompletableFuture.completedFuture(r);
+ })
+ .thenApply(v -> v.newValue());
});
}
@Override
public synchronized CompletableFuture<Void> addListener(MapEventListener<String, byte[]> listener,
- Executor executor) {
+ Executor executor) {
if (mapEventListeners.isEmpty()) {
- return client.submit(new Listen()).thenRun(() -> mapEventListeners.put(listener, executor));
+ return proxy.invoke(ADD_LISTENER).thenRun(() -> mapEventListeners.put(listener, executor));
} else {
mapEventListeners.put(listener, executor);
return CompletableFuture.completedFuture(null);
@@ -285,7 +325,7 @@
@Override
public synchronized CompletableFuture<Void> removeListener(MapEventListener<String, byte[]> listener) {
if (mapEventListeners.remove(listener) != null && mapEventListeners.isEmpty()) {
- return client.submit(new Unlisten()).thenApply(v -> null);
+ return proxy.invoke(REMOVE_LISTENER).thenApply(v -> null);
}
return CompletableFuture.completedFuture(null);
}
@@ -298,49 +338,55 @@
@Override
public CompletableFuture<Version> begin(TransactionId transactionId) {
- return client.submit(new TransactionBegin(transactionId)).thenApply(Version::new);
+ return proxy.<TransactionBegin, Long>invoke(
+ BEGIN,
+ SERIALIZER::encode,
+ new TransactionBegin(transactionId),
+ SERIALIZER::decode)
+ .thenApply(Version::new);
}
@Override
- public CompletableFuture<Boolean> prepare(
- TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
- return client.submit(new TransactionPrepare(transactionLog))
+ public CompletableFuture<Boolean> prepare(TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
+ return proxy.<TransactionPrepare, PrepareResult>invoke(
+ PREPARE,
+ SERIALIZER::encode,
+ new TransactionPrepare(transactionLog),
+ SERIALIZER::decode)
.thenApply(v -> v == PrepareResult.OK);
}
@Override
- public CompletableFuture<Boolean> prepareAndCommit(
- TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
- return client.submit(new TransactionPrepareAndCommit(transactionLog))
+ public CompletableFuture<Boolean> prepareAndCommit(TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
+ return proxy.<TransactionPrepareAndCommit, PrepareResult>invoke(
+ PREPARE_AND_COMMIT,
+ SERIALIZER::encode,
+ new TransactionPrepareAndCommit(transactionLog),
+ SERIALIZER::decode)
.thenApply(v -> v == PrepareResult.OK);
}
@Override
public CompletableFuture<Void> commit(TransactionId transactionId) {
- return client.submit(new TransactionCommit(transactionId)).thenApply(v -> null);
+ return proxy.<TransactionCommit, CommitResult>invoke(
+ COMMIT,
+ SERIALIZER::encode,
+ new TransactionCommit(transactionId),
+ SERIALIZER::decode)
+ .thenApply(v -> null);
}
@Override
public CompletableFuture<Void> rollback(TransactionId transactionId) {
- return client.submit(new TransactionRollback(transactionId)).thenApply(v -> null);
- }
-
- @Override
- public void addStatusChangeListener(Consumer<Status> listener) {
- statusChangeListeners.add(listener);
- }
-
- @Override
- public void removeStatusChangeListener(Consumer<Status> listener) {
- statusChangeListeners.remove(listener);
- }
-
- @Override
- public Collection<Consumer<Status>> statusChangeListeners() {
- return ImmutableSet.copyOf(statusChangeListeners);
+ return proxy.invoke(
+ ROLLBACK,
+ SERIALIZER::encode,
+ new TransactionRollback(transactionId),
+ SERIALIZER::decode)
+ .thenApply(v -> null);
}
private boolean isListening() {
return !mapEventListeners.isEmpty();
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java
deleted file mode 100644
index 8dd4096..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java
+++ /dev/null
@@ -1,632 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-import com.google.common.base.MoreObjects;
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.catalyst.util.Assert;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-import org.onlab.util.Match;
-import org.onosproject.store.primitives.MapUpdate;
-import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.service.TransactionLog;
-import org.onosproject.store.service.Versioned;
-
-/**
- * {@link AtomixConsistentMap} resource state machine operations.
- */
-public final class AtomixConsistentMapCommands {
-
- private AtomixConsistentMapCommands() {
- }
-
- /**
- * Abstract map command.
- */
- @SuppressWarnings("serial")
- public abstract static class MapCommand<V> implements Command<V>, CatalystSerializable {
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- /**
- * Abstract map query.
- */
- @SuppressWarnings("serial")
- public abstract static class MapQuery<V> implements Query<V>, CatalystSerializable {
-
- @Override
- public ConsistencyLevel consistency() {
- return ConsistencyLevel.SEQUENTIAL;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- /**
- * Abstract key-based query.
- */
- @SuppressWarnings("serial")
- public abstract static class KeyQuery<V> extends MapQuery<V> {
- protected String key;
-
- public KeyQuery() {
- }
-
- public KeyQuery(String key) {
- this.key = Assert.notNull(key, "key");
- }
-
- /**
- * Returns the key.
- * @return key
- */
- public String key() {
- return key;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- }
- }
-
- /**
- * Abstract value-based query.
- */
- @SuppressWarnings("serial")
- public abstract static class ValueQuery<V> extends MapQuery<V> {
- protected byte[] value;
-
- public ValueQuery() {
- }
-
- public ValueQuery(byte[] value) {
- this.value = Assert.notNull(value, "value");
- }
-
- /**
- * Returns the value.
- * @return value
- */
- public byte[] value() {
- return value;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("value", value)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(value, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- value = serializer.readObject(buffer);
- }
- }
-
- /**
- * Contains key command.
- */
- @SuppressWarnings("serial")
- public static class ContainsKey extends KeyQuery<Boolean> {
- public ContainsKey() {
- }
-
- public ContainsKey(String key) {
- super(key);
- }
- }
-
- /**
- * Contains value command.
- */
- @SuppressWarnings("serial")
- public static class ContainsValue extends ValueQuery<Boolean> {
- public ContainsValue() {
- }
-
- public ContainsValue(byte[] value) {
- super(value);
- }
- }
-
- /**
- * Transaction begin command.
- */
- public static class TransactionBegin extends MapCommand<Long> {
- private TransactionId transactionId;
-
- public TransactionBegin() {
- }
-
- public TransactionBegin(TransactionId transactionId) {
- this.transactionId = transactionId;
- }
-
- public TransactionId transactionId() {
- return transactionId;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(transactionId, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- transactionId = serializer.readObject(buffer);
- }
- }
-
- /**
- * Map prepare command.
- */
- @SuppressWarnings("serial")
- public static class TransactionPrepare extends MapCommand<PrepareResult> {
- private TransactionLog<MapUpdate<String, byte[]>> transactionLog;
-
- public TransactionPrepare() {
- }
-
- public TransactionPrepare(TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
- this.transactionLog = transactionLog;
- }
-
- public TransactionLog<MapUpdate<String, byte[]>> transactionLog() {
- return transactionLog;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(transactionLog, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- transactionLog = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("transactionLog", transactionLog)
- .toString();
- }
- }
-
- /**
- * Map prepareAndCommit command.
- */
- @SuppressWarnings("serial")
- public static class TransactionPrepareAndCommit extends TransactionPrepare {
- public TransactionPrepareAndCommit() {
- }
-
- public TransactionPrepareAndCommit(TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
- super(transactionLog);
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Map transaction commit command.
- */
- @SuppressWarnings("serial")
- public static class TransactionCommit extends MapCommand<CommitResult> {
- private TransactionId transactionId;
-
- public TransactionCommit() {
- }
-
- public TransactionCommit(TransactionId transactionId) {
- this.transactionId = transactionId;
- }
-
- /**
- * Returns the transaction identifier.
- * @return transaction id
- */
- public TransactionId transactionId() {
- return transactionId;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(transactionId, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- transactionId = serializer.readObject(buffer);
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("transactionId", transactionId)
- .toString();
- }
- }
-
- /**
- * Map transaction rollback command.
- */
- @SuppressWarnings("serial")
- public static class TransactionRollback extends MapCommand<RollbackResult> {
- private TransactionId transactionId;
-
- public TransactionRollback() {
- }
-
- public TransactionRollback(TransactionId transactionId) {
- this.transactionId = transactionId;
- }
-
- /**
- * Returns the transaction identifier.
- * @return transaction id
- */
- public TransactionId transactionId() {
- return transactionId;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(transactionId, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- transactionId = serializer.readObject(buffer);
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("transactionId", transactionId)
- .toString();
- }
- }
-
- /**
- * Map update command.
- */
- @SuppressWarnings("serial")
- public static class UpdateAndGet extends MapCommand<MapEntryUpdateResult<String, byte[]>> {
- private String key;
- private byte[] value;
- private Match<byte[]> valueMatch;
- private Match<Long> versionMatch;
-
- public UpdateAndGet() {
- }
-
- public UpdateAndGet(String key,
- byte[] value,
- Match<byte[]> valueMatch,
- Match<Long> versionMatch) {
- this.key = key;
- this.value = value;
- this.valueMatch = valueMatch;
- this.versionMatch = versionMatch;
- }
-
- /**
- * Returns the key.
- * @return key
- */
- public String key() {
- return this.key;
- }
-
- /**
- * Returns the value.
- * @return value
- */
- public byte[] value() {
- return this.value;
- }
-
- /**
- * Returns the value match.
- * @return value match
- */
- public Match<byte[]> valueMatch() {
- return this.valueMatch;
- }
-
- /**
- * Returns the version match.
- * @return version match
- */
- public Match<Long> versionMatch() {
- return this.versionMatch;
- }
-
- @Override
- public CompactionMode compaction() {
- return value == null ? CompactionMode.TOMBSTONE : CompactionMode.FULL;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(value, buffer);
- serializer.writeObject(valueMatch, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- value = serializer.readObject(buffer);
- valueMatch = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("value", value)
- .add("valueMatch", valueMatch)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Get query.
- */
- @SuppressWarnings("serial")
- public static class Get extends KeyQuery<Versioned<byte[]>> {
- public Get() {
- }
-
- public Get(String key) {
- super(key);
- }
- }
-
- /**
- * Get or default query.
- */
- @SuppressWarnings("serial")
- public static class GetOrDefault extends KeyQuery<Versioned<byte[]>> {
- private byte[] defaultValue;
-
- public GetOrDefault() {
- }
-
- public GetOrDefault(String key, byte[] defaultValue) {
- super(key);
- this.defaultValue = defaultValue;
- }
-
- /**
- * Returns the default value.
- *
- * @return the default value
- */
- public byte[] defaultValue() {
- return defaultValue;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(defaultValue, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- defaultValue = serializer.readObject(buffer);
- }
- }
-
- /**
- * Is empty query.
- */
- @SuppressWarnings("serial")
- public static class IsEmpty extends MapQuery<Boolean> {
- }
-
- /**
- * KeySet query.
- */
- @SuppressWarnings("serial")
- public static class KeySet extends MapQuery<Set<String>> {
- }
-
- /**
- * ValueSet query.
- */
- @SuppressWarnings("serial")
- public static class Values extends MapQuery<Collection<Versioned<byte[]>>> {
- }
-
- /**
- * EntrySet query.
- */
- @SuppressWarnings("serial")
- public static class EntrySet extends MapQuery<Set<Map.Entry<String, Versioned<byte[]>>>> {
- }
-
- /**
- * Size query.
- */
- @SuppressWarnings("serial")
- public static class Size extends MapQuery<Integer> {
- }
-
- /**
- * Clear command.
- */
- @SuppressWarnings("serial")
- public static class Clear extends MapCommand<MapEntryUpdateResult.Status> {
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Change listen.
- */
- @SuppressWarnings("serial")
- public static class Listen implements Command<Void>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
- }
-
- /**
- * Change unlisten.
- */
- @SuppressWarnings("serial")
- public static class Unlisten implements Command<Void>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Map command type resolver.
- */
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- registry.register(ContainsKey.class, -761);
- registry.register(ContainsValue.class, -762);
- registry.register(Get.class, -763);
- registry.register(GetOrDefault.class, -778);
- registry.register(EntrySet.class, -764);
- registry.register(Values.class, -765);
- registry.register(KeySet.class, -766);
- registry.register(Clear.class, -767);
- registry.register(IsEmpty.class, -768);
- registry.register(Size.class, -769);
- registry.register(Listen.class, -770);
- registry.register(Unlisten.class, -771);
- registry.register(TransactionBegin.class, -777);
- registry.register(TransactionPrepare.class, -772);
- registry.register(TransactionCommit.class, -773);
- registry.register(TransactionRollback.class, -774);
- registry.register(TransactionPrepareAndCommit.class, -775);
- registry.register(UpdateAndGet.class, -776);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java
new file mode 100644
index 0000000..f0e6a42
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.event.EventType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.MapEvent;
+
+/**
+ * Atomix consistent map events.
+ */
+public enum AtomixConsistentMapEvents implements EventType {
+ CHANGE("change");
+
+ private final String id;
+
+ AtomixConsistentMapEvents(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50)
+ .register(MapEvent.class)
+ .register(MapEvent.Type.class)
+ .register(byte[].class)
+ .build("AtomixConsistentMapEvents");
+
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java
deleted file mode 100644
index bf1523a..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * {@link AtomixConsistentMap} resource factory.
- *
- */
-public class AtomixConsistentMapFactory implements ResourceFactory<AtomixConsistentMap> {
-
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixConsistentMapCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixConsistentMapState(config);
- }
-
- @Override
- public AtomixConsistentMap createInstance(CopycatClient client, Properties options) {
- return new AtomixConsistentMap(client, options);
- }
- }
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java
new file mode 100644
index 0000000..2d81787
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import com.google.common.base.MoreObjects;
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.primitives.MapUpdate;
+import org.onosproject.store.primitives.TransactionId;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.TransactionLog;
+import org.onosproject.store.service.Versioned;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link AtomixConsistentMap} resource state machine operations.
+ */
+public enum AtomixConsistentMapOperations implements OperationId {
+ IS_EMPTY("isEmpty", OperationType.QUERY),
+ SIZE("size", OperationType.QUERY),
+ CONTAINS_KEY("containsKey", OperationType.QUERY),
+ CONTAINS_VALUE("containsValue", OperationType.QUERY),
+ GET("get", OperationType.QUERY),
+ GET_OR_DEFAULT("getOrDefault", OperationType.QUERY),
+ KEY_SET("keySet", OperationType.QUERY),
+ VALUES("values", OperationType.QUERY),
+ ENTRY_SET("entrySet", OperationType.QUERY),
+ UPDATE_AND_GET("updateAndGet", OperationType.COMMAND),
+ CLEAR("clear", OperationType.COMMAND),
+ ADD_LISTENER("addListener", OperationType.COMMAND),
+ REMOVE_LISTENER("removeListener", OperationType.COMMAND),
+ BEGIN("begin", OperationType.COMMAND),
+ PREPARE("prepare", OperationType.COMMAND),
+ PREPARE_AND_COMMIT("prepareAndCommit", OperationType.COMMAND),
+ COMMIT("commit", OperationType.COMMAND),
+ ROLLBACK("rollback", OperationType.COMMAND);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixConsistentMapOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(ContainsKey.class)
+ .register(ContainsValue.class)
+ .register(Get.class)
+ .register(GetOrDefault.class)
+ .register(UpdateAndGet.class)
+ .register(TransactionBegin.class)
+ .register(TransactionPrepare.class)
+ .register(TransactionPrepareAndCommit.class)
+ .register(TransactionCommit.class)
+ .register(TransactionRollback.class)
+ .register(TransactionId.class)
+ .register(TransactionLog.class)
+ .register(MapUpdate.class)
+ .register(MapUpdate.Type.class)
+ .register(PrepareResult.class)
+ .register(CommitResult.class)
+ .register(RollbackResult.class)
+ .register(Match.class)
+ .register(MapEntryUpdateResult.class)
+ .register(MapEntryUpdateResult.Status.class)
+ .register(Versioned.class)
+ .register(byte[].class)
+ .build("AtomixConsistentMapOperations");
+
+ /**
+ * Abstract map command.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class MapOperation {
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .toString();
+ }
+ }
+
+ /**
+ * Abstract key-based query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class KeyOperation extends MapOperation {
+ protected String key;
+
+ public KeyOperation() {
+ }
+
+ public KeyOperation(String key) {
+ this.key = checkNotNull(key, "key cannot be null");
+ }
+
+ /**
+ * Returns the key.
+ * @return key
+ */
+ public String key() {
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .toString();
+ }
+ }
+
+ /**
+ * Abstract value-based query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class ValueOperation extends MapOperation {
+ protected byte[] value;
+
+ public ValueOperation() {
+ }
+
+ public ValueOperation(byte[] value) {
+ this.value = checkNotNull(value, "value cannot be null");
+ }
+
+ /**
+ * Returns the value.
+ * @return value
+ */
+ public byte[] value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("value", value)
+ .toString();
+ }
+ }
+
+ /**
+ * Contains key command.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsKey extends KeyOperation {
+ public ContainsKey() {
+ }
+
+ public ContainsKey(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Contains value command.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsValue extends ValueOperation {
+ public ContainsValue() {
+ }
+
+ public ContainsValue(byte[] value) {
+ super(value);
+ }
+ }
+
+ /**
+ * Transaction begin command.
+ */
+ public static class TransactionBegin extends MapOperation {
+ private TransactionId transactionId;
+
+ public TransactionBegin() {
+ }
+
+ public TransactionBegin(TransactionId transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ public TransactionId transactionId() {
+ return transactionId;
+ }
+ }
+
+ /**
+ * Map prepare command.
+ */
+ @SuppressWarnings("serial")
+ public static class TransactionPrepare extends MapOperation {
+ private TransactionLog<MapUpdate<String, byte[]>> transactionLog;
+
+ public TransactionPrepare() {
+ }
+
+ public TransactionPrepare(TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
+ this.transactionLog = transactionLog;
+ }
+
+ public TransactionLog<MapUpdate<String, byte[]>> transactionLog() {
+ return transactionLog;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("transactionLog", transactionLog)
+ .toString();
+ }
+ }
+
+ /**
+ * Map prepareAndCommit command.
+ */
+ @SuppressWarnings("serial")
+ public static class TransactionPrepareAndCommit extends TransactionPrepare {
+ public TransactionPrepareAndCommit() {
+ }
+
+ public TransactionPrepareAndCommit(TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
+ super(transactionLog);
+ }
+ }
+
+ /**
+ * Map transaction commit command.
+ */
+ @SuppressWarnings("serial")
+ public static class TransactionCommit extends MapOperation {
+ private TransactionId transactionId;
+
+ public TransactionCommit() {
+ }
+
+ public TransactionCommit(TransactionId transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ /**
+ * Returns the transaction identifier.
+ * @return transaction id
+ */
+ public TransactionId transactionId() {
+ return transactionId;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("transactionId", transactionId)
+ .toString();
+ }
+ }
+
+ /**
+ * Map transaction rollback command.
+ */
+ @SuppressWarnings("serial")
+ public static class TransactionRollback extends MapOperation {
+ private TransactionId transactionId;
+
+ public TransactionRollback() {
+ }
+
+ public TransactionRollback(TransactionId transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ /**
+ * Returns the transaction identifier.
+ * @return transaction id
+ */
+ public TransactionId transactionId() {
+ return transactionId;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("transactionId", transactionId)
+ .toString();
+ }
+ }
+
+ /**
+ * Map update command.
+ */
+ @SuppressWarnings("serial")
+ public static class UpdateAndGet extends MapOperation {
+ private String key;
+ private byte[] value;
+ private Match<byte[]> valueMatch;
+ private Match<Long> versionMatch;
+
+ public UpdateAndGet() {
+ }
+
+ public UpdateAndGet(String key,
+ byte[] value,
+ Match<byte[]> valueMatch,
+ Match<Long> versionMatch) {
+ this.key = key;
+ this.value = value;
+ this.valueMatch = valueMatch;
+ this.versionMatch = versionMatch;
+ }
+
+ /**
+ * Returns the key.
+ * @return key
+ */
+ public String key() {
+ return this.key;
+ }
+
+ /**
+ * Returns the value.
+ * @return value
+ */
+ public byte[] value() {
+ return this.value;
+ }
+
+ /**
+ * Returns the value match.
+ * @return value match
+ */
+ public Match<byte[]> valueMatch() {
+ return this.valueMatch;
+ }
+
+ /**
+ * Returns the version match.
+ * @return version match
+ */
+ public Match<Long> versionMatch() {
+ return this.versionMatch;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("value", value)
+ .add("valueMatch", valueMatch)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Get query.
+ */
+ @SuppressWarnings("serial")
+ public static class Get extends KeyOperation {
+ public Get() {
+ }
+
+ public Get(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Get or default query.
+ */
+ @SuppressWarnings("serial")
+ public static class GetOrDefault extends KeyOperation {
+ private byte[] defaultValue;
+
+ public GetOrDefault() {
+ }
+
+ public GetOrDefault(String key, byte[] defaultValue) {
+ super(key);
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Returns the default value.
+ *
+ * @return the default value
+ */
+ public byte[] defaultValue() {
+ return defaultValue;
+ }
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapService.java
new file mode 100644
index 0000000..6d8c1b0
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapService.java
@@ -0,0 +1,743 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.session.RaftSession;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.primitives.MapUpdate;
+import org.onosproject.store.primitives.TransactionId;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsKey;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsValue;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.Get;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GetOrDefault;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionBegin;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionCommit;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepare;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepareAndCommit;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionRollback;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UpdateAndGet;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.TransactionLog;
+import org.onosproject.store.service.Versioned;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.BEGIN;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.COMMIT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ENTRY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET_OR_DEFAULT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.KEY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE_AND_COMMIT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ROLLBACK;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.SIZE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UPDATE_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.VALUES;
+import static org.onosproject.store.service.MapEvent.Type.INSERT;
+import static org.onosproject.store.service.MapEvent.Type.REMOVE;
+import static org.onosproject.store.service.MapEvent.Type.UPDATE;
+
+/**
+ * State Machine for {@link AtomixConsistentMap} resource.
+ */
+public class AtomixConsistentMapService extends AbstractRaftService {
+
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixConsistentMapOperations.NAMESPACE)
+ .register(AtomixConsistentMapEvents.NAMESPACE)
+ .nextId(KryoNamespace.FLOATING_ID)
+ .register(TransactionScope.class)
+ .register(TransactionLog.class)
+ .register(TransactionId.class)
+ .register(MapEntryValue.class)
+ .register(MapEntryValue.Type.class)
+ .register(new HashMap().keySet().getClass())
+ .build());
+
+ private Map<Long, RaftSession> listeners = new LinkedHashMap<>();
+ private Map<String, MapEntryValue> mapEntries = new HashMap<>();
+ private Set<String> preparedKeys = Sets.newHashSet();
+ private Map<TransactionId, TransactionScope> activeTransactions = Maps.newHashMap();
+ private long currentVersion;
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeObject(Sets.newHashSet(listeners.keySet()), SERIALIZER::encode);
+ writer.writeObject(preparedKeys, SERIALIZER::encode);
+ writer.writeObject(mapEntries, SERIALIZER::encode);
+ writer.writeObject(activeTransactions, SERIALIZER::encode);
+ writer.writeLong(currentVersion);
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ listeners = new LinkedHashMap<>();
+ for (Long sessionId : reader.<Set<Long>>readObject(SERIALIZER::decode)) {
+ listeners.put(sessionId, getSessions().getSession(sessionId));
+ }
+ preparedKeys = reader.readObject(SERIALIZER::decode);
+ mapEntries = reader.readObject(SERIALIZER::decode);
+ activeTransactions = reader.readObject(SERIALIZER::decode);
+ currentVersion = reader.readLong();
+ }
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
+ // Listeners
+ executor.register(ADD_LISTENER, this::listen);
+ executor.register(REMOVE_LISTENER, this::unlisten);
+ // Queries
+ executor.register(CONTAINS_KEY, SERIALIZER::decode, this::containsKey, SERIALIZER::encode);
+ executor.register(CONTAINS_VALUE, SERIALIZER::decode, this::containsValue, SERIALIZER::encode);
+ executor.register(ENTRY_SET, this::entrySet, SERIALIZER::encode);
+ executor.register(GET, SERIALIZER::decode, this::get, SERIALIZER::encode);
+ executor.register(GET_OR_DEFAULT, SERIALIZER::decode, this::getOrDefault, SERIALIZER::encode);
+ executor.register(IS_EMPTY, this::isEmpty, SERIALIZER::encode);
+ executor.register(KEY_SET, this::keySet, SERIALIZER::encode);
+ executor.register(SIZE, this::size, SERIALIZER::encode);
+ executor.register(VALUES, this::values, SERIALIZER::encode);
+ // Commands
+ executor.register(UPDATE_AND_GET, SERIALIZER::decode, this::updateAndGet, SERIALIZER::encode);
+ executor.register(CLEAR, this::clear, SERIALIZER::encode);
+ executor.register(BEGIN, SERIALIZER::decode, this::begin, SERIALIZER::encode);
+ executor.register(PREPARE, SERIALIZER::decode, this::prepare, SERIALIZER::encode);
+ executor.register(PREPARE_AND_COMMIT, SERIALIZER::decode, this::prepareAndCommit, SERIALIZER::encode);
+ executor.register(COMMIT, SERIALIZER::decode, this::commit, SERIALIZER::encode);
+ executor.register(ROLLBACK, SERIALIZER::decode, this::rollback, SERIALIZER::encode);
+ }
+
+ /**
+ * Handles a contains key commit.
+ *
+ * @param commit containsKey commit
+ * @return {@code true} if map contains key
+ */
+ protected boolean containsKey(Commit<? extends ContainsKey> commit) {
+ MapEntryValue value = mapEntries.get(commit.value().key());
+ return value != null && value.type() != MapEntryValue.Type.TOMBSTONE;
+ }
+
+ /**
+ * Handles a contains value commit.
+ *
+ * @param commit containsValue commit
+ * @return {@code true} if map contains value
+ */
+ protected boolean containsValue(Commit<? extends ContainsValue> commit) {
+ Match<byte[]> valueMatch = Match.ifValue(commit.value().value());
+ return mapEntries.values().stream()
+ .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE)
+ .anyMatch(value -> valueMatch.matches(value.value()));
+ }
+
+ /**
+ * Handles a get commit.
+ *
+ * @param commit get commit
+ * @return value mapped to key
+ */
+ protected Versioned<byte[]> get(Commit<? extends Get> commit) {
+ return toVersioned(mapEntries.get(commit.value().key()));
+ }
+
+ /**
+ * Handles a get or default commit.
+ *
+ * @param commit get or default commit
+ * @return value mapped to key
+ */
+ protected Versioned<byte[]> getOrDefault(Commit<? extends GetOrDefault> commit) {
+ MapEntryValue value = mapEntries.get(commit.value().key());
+ if (value == null) {
+ return new Versioned<>(commit.value().defaultValue(), 0);
+ } else if (value.type() == MapEntryValue.Type.TOMBSTONE) {
+ return new Versioned<>(commit.value().defaultValue(), value.version);
+ } else {
+ return new Versioned<>(value.value(), value.version);
+ }
+ }
+
+ /**
+ * Handles a count commit.
+ *
+ * @param commit size commit
+ * @return number of entries in map
+ */
+ protected int size(Commit<Void> commit) {
+ return (int) mapEntries.values().stream()
+ .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE)
+ .count();
+ }
+
+ /**
+ * Handles an is empty commit.
+ *
+ * @param commit isEmpty commit
+ * @return {@code true} if map is empty
+ */
+ protected boolean isEmpty(Commit<Void> commit) {
+ return mapEntries.values().stream()
+ .noneMatch(value -> value.type() != MapEntryValue.Type.TOMBSTONE);
+ }
+
+ /**
+ * Handles a keySet commit.
+ *
+ * @param commit keySet commit
+ * @return set of keys in map
+ */
+ protected Set<String> keySet(Commit<Void> commit) {
+ return mapEntries.entrySet().stream()
+ .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Handles a values commit.
+ *
+ * @param commit values commit
+ * @return collection of values in map
+ */
+ protected Collection<Versioned<byte[]>> values(Commit<Void> commit) {
+ return mapEntries.entrySet().stream()
+ .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE)
+ .map(entry -> toVersioned(entry.getValue()))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Handles a entry set commit.
+ *
+ * @param commit entrySet commit
+ * @return set of map entries
+ */
+ protected Set<Map.Entry<String, Versioned<byte[]>>> entrySet(Commit<Void> commit) {
+ return mapEntries.entrySet().stream()
+ .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE)
+ .map(e -> Maps.immutableEntry(e.getKey(), toVersioned(e.getValue())))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Handles a update and get commit.
+ *
+ * @param commit updateAndGet commit
+ * @return update result
+ */
+ protected MapEntryUpdateResult<String, byte[]> updateAndGet(Commit<? extends UpdateAndGet> commit) {
+ try {
+ MapEntryUpdateResult.Status updateStatus = validate(commit.value());
+ String key = commit.value().key();
+ MapEntryValue oldCommitValue = mapEntries.get(commit.value().key());
+ Versioned<byte[]> oldMapValue = toVersioned(oldCommitValue);
+
+ if (updateStatus != MapEntryUpdateResult.Status.OK) {
+ return new MapEntryUpdateResult<>(updateStatus, "", key, oldMapValue, oldMapValue);
+ }
+
+ byte[] newValue = commit.value().value();
+ currentVersion = commit.index();
+ Versioned<byte[]> newMapValue = newValue == null ? null
+ : new Versioned<>(newValue, currentVersion);
+
+ MapEvent.Type updateType = newValue == null ? REMOVE
+ : oldCommitValue == null ? INSERT : UPDATE;
+
+ // If a value existed in the map, remove and discard the value to ensure disk can be freed.
+ if (updateType == REMOVE || updateType == UPDATE) {
+ mapEntries.remove(key);
+ }
+
+ // If this is an insert/update commit, add the commit to the map entries.
+ if (updateType == INSERT || updateType == UPDATE) {
+ mapEntries.put(key, new MapEntryValue(
+ MapEntryValue.Type.VALUE,
+ commit.index(),
+ commit.value().value()));
+ } else if (!activeTransactions.isEmpty()) {
+ // If this is a delete but transactions are currently running, ensure tombstones are retained
+ // for version checks.
+ mapEntries.put(key, new MapEntryValue(MapEntryValue.Type.TOMBSTONE, commit.index(), null));
+ }
+
+ publish(Lists.newArrayList(new MapEvent<>("", key, newMapValue, oldMapValue)));
+ return new MapEntryUpdateResult<>(updateStatus, "", key, oldMapValue, newMapValue);
+ } catch (Exception e) {
+ getLogger().error("State machine operation failed", e);
+ throw Throwables.propagate(e);
+ }
+ }
+
+ /**
+ * Handles a clear commit.
+ *
+ * @param commit clear commit
+ * @return clear result
+ */
+ protected MapEntryUpdateResult.Status clear(Commit<Void> commit) {
+ Iterator<Map.Entry<String, MapEntryValue>> iterator = mapEntries
+ .entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, MapEntryValue> entry = iterator.next();
+ String key = entry.getKey();
+ MapEntryValue value = entry.getValue();
+ Versioned<byte[]> removedValue = new Versioned<>(value.value(),
+ value.version());
+ publish(Lists.newArrayList(new MapEvent<>("", key, null, removedValue)));
+ iterator.remove();
+ }
+ return MapEntryUpdateResult.Status.OK;
+ }
+
+ /**
+ * Handles a listen commit.
+ *
+ * @param commit listen commit
+ */
+ protected void listen(Commit<Void> commit) {
+ listeners.put(commit.session().sessionId().id(), commit.session());
+ }
+
+ /**
+ * Handles an unlisten commit.
+ *
+ * @param commit unlisten commit
+ */
+ protected void unlisten(Commit<Void> commit) {
+ listeners.remove(commit.session().sessionId().id());
+ }
+
+ /**
+ * Handles a begin commit.
+ *
+ * @param commit transaction begin commit
+ * @return transaction state version
+ */
+ protected long begin(Commit<? extends TransactionBegin> commit) {
+ long version = commit.index();
+ activeTransactions.put(commit.value().transactionId(), new TransactionScope(version));
+ return version;
+ }
+
+ /**
+ * Handles an prepare and commit commit.
+ *
+ * @param commit transaction prepare and commit commit
+ * @return prepare result
+ */
+ protected PrepareResult prepareAndCommit(Commit<? extends TransactionPrepareAndCommit> commit) {
+ TransactionId transactionId = commit.value().transactionLog().transactionId();
+ PrepareResult prepareResult = prepare(commit);
+ TransactionScope transactionScope = activeTransactions.remove(transactionId);
+ if (prepareResult == PrepareResult.OK) {
+ this.currentVersion = commit.index();
+ transactionScope = transactionScope.prepared(commit);
+ commitTransaction(transactionScope);
+ }
+ discardTombstones();
+ return prepareResult;
+ }
+
+ /**
+ * Handles an prepare commit.
+ *
+ * @param commit transaction prepare commit
+ * @return prepare result
+ */
+ protected PrepareResult prepare(Commit<? extends TransactionPrepare> commit) {
+ try {
+ TransactionLog<MapUpdate<String, byte[]>> transactionLog = commit.value().transactionLog();
+
+ // Iterate through records in the transaction log and perform isolation checks.
+ for (MapUpdate<String, byte[]> record : transactionLog.records()) {
+ String key = record.key();
+
+ // If the record is a VERSION_MATCH then check that the record's version matches the current
+ // version of the state machine.
+ if (record.type() == MapUpdate.Type.VERSION_MATCH && key == null) {
+ if (record.version() > currentVersion) {
+ return PrepareResult.OPTIMISTIC_LOCK_FAILURE;
+ } else {
+ continue;
+ }
+ }
+
+ // If the prepared keys already contains the key contained within the record, that indicates a
+ // conflict with a concurrent transaction.
+ if (preparedKeys.contains(key)) {
+ return PrepareResult.CONCURRENT_TRANSACTION;
+ }
+
+ // Read the existing value from the map.
+ MapEntryValue existingValue = mapEntries.get(key);
+
+ // Note: if the existing value is null, that means the key has not changed during the transaction,
+ // otherwise a tombstone would have been retained.
+ if (existingValue == null) {
+ // If the value is null, ensure the version is equal to the transaction version.
+ if (record.version() != transactionLog.version()) {
+ return PrepareResult.OPTIMISTIC_LOCK_FAILURE;
+ }
+ } else {
+ // If the value is non-null, compare the current version with the record version.
+ if (existingValue.version() > record.version()) {
+ return PrepareResult.OPTIMISTIC_LOCK_FAILURE;
+ }
+ }
+ }
+
+ // No violations detected. Mark modified keys locked for transactions.
+ transactionLog.records().forEach(record -> {
+ if (record.type() != MapUpdate.Type.VERSION_MATCH) {
+ preparedKeys.add(record.key());
+ }
+ });
+
+ // Update the transaction scope. If the transaction scope is not set on this node, that indicates the
+ // coordinator is communicating with another node. Transactions assume that the client is communicating
+ // with a single leader in order to limit the overhead of retaining tombstones.
+ TransactionScope transactionScope = activeTransactions.get(transactionLog.transactionId());
+ if (transactionScope == null) {
+ activeTransactions.put(
+ transactionLog.transactionId(),
+ new TransactionScope(transactionLog.version(), commit.value().transactionLog()));
+ return PrepareResult.PARTIAL_FAILURE;
+ } else {
+ activeTransactions.put(
+ transactionLog.transactionId(),
+ transactionScope.prepared(commit));
+ return PrepareResult.OK;
+ }
+ } catch (Exception e) {
+ getLogger().warn("Failure applying {}", commit, e);
+ throw Throwables.propagate(e);
+ }
+ }
+
+ /**
+ * Handles an commit commit (ha!).
+ *
+ * @param commit transaction commit commit
+ * @return commit result
+ */
+ protected CommitResult commit(Commit<? extends TransactionCommit> commit) {
+ TransactionId transactionId = commit.value().transactionId();
+ TransactionScope transactionScope = activeTransactions.remove(transactionId);
+ if (transactionScope == null) {
+ return CommitResult.UNKNOWN_TRANSACTION_ID;
+ }
+
+ try {
+ this.currentVersion = commit.index();
+ return commitTransaction(transactionScope);
+ } catch (Exception e) {
+ getLogger().warn("Failure applying {}", commit, e);
+ throw Throwables.propagate(e);
+ } finally {
+ discardTombstones();
+ }
+ }
+
+ /**
+ * Applies committed operations to the state machine.
+ */
+ private CommitResult commitTransaction(TransactionScope transactionScope) {
+ TransactionLog<MapUpdate<String, byte[]>> transactionLog = transactionScope.transactionLog();
+ boolean retainTombstones = !activeTransactions.isEmpty();
+
+ List<MapEvent<String, byte[]>> eventsToPublish = Lists.newArrayList();
+ for (MapUpdate<String, byte[]> record : transactionLog.records()) {
+ if (record.type() == MapUpdate.Type.VERSION_MATCH) {
+ continue;
+ }
+
+ String key = record.key();
+ checkState(preparedKeys.remove(key), "key is not prepared");
+
+ if (record.type() == MapUpdate.Type.LOCK) {
+ continue;
+ }
+
+ MapEntryValue previousValue = mapEntries.remove(key);
+ MapEntryValue newValue = null;
+
+ // If the record is not a delete, create a transactional commit.
+ if (record.type() != MapUpdate.Type.REMOVE_IF_VERSION_MATCH) {
+ newValue = new MapEntryValue(MapEntryValue.Type.VALUE, currentVersion, record.value());
+ } else if (retainTombstones) {
+ // For deletes, if tombstones need to be retained then create and store a tombstone commit.
+ newValue = new MapEntryValue(MapEntryValue.Type.TOMBSTONE, currentVersion, null);
+ }
+
+ eventsToPublish.add(new MapEvent<>("", key, toVersioned(newValue), toVersioned(previousValue)));
+
+ if (newValue != null) {
+ mapEntries.put(key, newValue);
+ }
+ }
+ publish(eventsToPublish);
+ return CommitResult.OK;
+ }
+
+ /**
+ * Handles an rollback commit (ha!).
+ *
+ * @param commit transaction rollback commit
+ * @return rollback result
+ */
+ protected RollbackResult rollback(Commit<? extends TransactionRollback> commit) {
+ TransactionId transactionId = commit.value().transactionId();
+ TransactionScope transactionScope = activeTransactions.remove(transactionId);
+ if (transactionScope == null) {
+ return RollbackResult.UNKNOWN_TRANSACTION_ID;
+ } else if (!transactionScope.isPrepared()) {
+ discardTombstones();
+ return RollbackResult.OK;
+ } else {
+ try {
+ transactionScope.transactionLog().records()
+ .forEach(record -> {
+ if (record.type() != MapUpdate.Type.VERSION_MATCH) {
+ preparedKeys.remove(record.key());
+ }
+ });
+ return RollbackResult.OK;
+ } finally {
+ discardTombstones();
+ }
+ }
+
+ }
+
+ /**
+ * Discards tombstones no longer needed by active transactions.
+ */
+ private void discardTombstones() {
+ if (activeTransactions.isEmpty()) {
+ Iterator<Map.Entry<String, MapEntryValue>> iterator = mapEntries.entrySet().iterator();
+ while (iterator.hasNext()) {
+ MapEntryValue value = iterator.next().getValue();
+ if (value.type() == MapEntryValue.Type.TOMBSTONE) {
+ iterator.remove();
+ }
+ }
+ } else {
+ long lowWaterMark = activeTransactions.values().stream()
+ .mapToLong(TransactionScope::version)
+ .min().getAsLong();
+ Iterator<Map.Entry<String, MapEntryValue>> iterator = mapEntries.entrySet().iterator();
+ while (iterator.hasNext()) {
+ MapEntryValue value = iterator.next().getValue();
+ if (value.type() == MapEntryValue.Type.TOMBSTONE && value.version < lowWaterMark) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Computes the update status that would result if the specified update were to applied to
+ * the state machine.
+ *
+ * @param update update
+ * @return status
+ */
+ private MapEntryUpdateResult.Status validate(UpdateAndGet update) {
+ MapEntryValue existingValue = mapEntries.get(update.key());
+ boolean isEmpty = existingValue == null || existingValue.type() == MapEntryValue.Type.TOMBSTONE;
+ if (isEmpty && update.value() == null) {
+ return MapEntryUpdateResult.Status.NOOP;
+ }
+ if (preparedKeys.contains(update.key())) {
+ return MapEntryUpdateResult.Status.WRITE_LOCK;
+ }
+ byte[] existingRawValue = isEmpty ? null : existingValue.value();
+ Long existingVersion = isEmpty ? null : existingValue.version();
+ return update.valueMatch().matches(existingRawValue)
+ && update.versionMatch().matches(existingVersion) ? MapEntryUpdateResult.Status.OK
+ : MapEntryUpdateResult.Status.PRECONDITION_FAILED;
+ }
+
+ /**
+ * Utility for turning a {@code MapEntryValue} to {@code Versioned}.
+ * @param value map entry value
+ * @return versioned instance
+ */
+ private Versioned<byte[]> toVersioned(MapEntryValue value) {
+ return value != null && value.type() != MapEntryValue.Type.TOMBSTONE
+ ? new Versioned<>(value.value(), value.version()) : null;
+ }
+
+ /**
+ * Publishes events to listeners.
+ *
+ * @param events list of map event to publish
+ */
+ private void publish(List<MapEvent<String, byte[]>> events) {
+ listeners.values().forEach(session -> {
+ session.publish(CHANGE, SERIALIZER::encode, events);
+ });
+ }
+
+ @Override
+ public void onExpire(RaftSession session) {
+ closeListener(session.sessionId().id());
+ }
+
+ @Override
+ public void onClose(RaftSession session) {
+ closeListener(session.sessionId().id());
+ }
+
+ private void closeListener(Long sessionId) {
+ listeners.remove(sessionId);
+ }
+
+ /**
+ * Interface implemented by map values.
+ */
+ private static class MapEntryValue {
+ protected final Type type;
+ protected final long version;
+ protected final byte[] value;
+
+ MapEntryValue(Type type, long version, byte[] value) {
+ this.type = type;
+ this.version = version;
+ this.value = value;
+ }
+
+ /**
+ * Returns the value type.
+ *
+ * @return the value type
+ */
+ Type type() {
+ return type;
+ }
+
+ /**
+ * Returns the version of the value.
+ *
+ * @return version
+ */
+ long version() {
+ return version;
+ }
+
+ /**
+ * Returns the raw {@code byte[]}.
+ *
+ * @return raw value
+ */
+ byte[] value() {
+ return value;
+ }
+
+ /**
+ * Value type.
+ */
+ enum Type {
+ VALUE,
+ TOMBSTONE,
+ }
+ }
+
+ /**
+ * Map transaction scope.
+ */
+ private static final class TransactionScope {
+ private final long version;
+ private final TransactionLog<MapUpdate<String, byte[]>> transactionLog;
+
+ private TransactionScope(long version) {
+ this(version, null);
+ }
+
+ private TransactionScope(long version, TransactionLog<MapUpdate<String, byte[]>> transactionLog) {
+ this.version = version;
+ this.transactionLog = transactionLog;
+ }
+
+ /**
+ * Returns the transaction version.
+ *
+ * @return the transaction version
+ */
+ long version() {
+ return version;
+ }
+
+ /**
+ * Returns whether this is a prepared transaction scope.
+ *
+ * @return whether this is a prepared transaction scope
+ */
+ boolean isPrepared() {
+ return transactionLog != null;
+ }
+
+ /**
+ * Returns the transaction commit log.
+ *
+ * @return the transaction commit log
+ */
+ TransactionLog<MapUpdate<String, byte[]>> transactionLog() {
+ checkState(isPrepared());
+ return transactionLog;
+ }
+
+ /**
+ * Returns a new transaction scope with a prepare commit.
+ *
+ * @param commit the prepare commit
+ * @return new transaction scope updated with the prepare commit
+ */
+ TransactionScope prepared(Commit<? extends TransactionPrepare> commit) {
+ return new TransactionScope(version, commit.value().transactionLog());
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapState.java
deleted file mode 100644
index ea62e72..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapState.java
+++ /dev/null
@@ -1,946 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import static com.google.common.base.Preconditions.checkState;
-import static org.onosproject.store.service.MapEvent.Type.INSERT;
-import static org.onosproject.store.service.MapEvent.Type.REMOVE;
-import static org.onosproject.store.service.MapEvent.Type.UPDATE;
-import static org.slf4j.LoggerFactory.getLogger;
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.Snapshottable;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.session.ServerSession;
-import io.atomix.copycat.server.session.SessionListener;
-import io.atomix.copycat.server.storage.snapshot.SnapshotReader;
-import io.atomix.copycat.server.storage.snapshot.SnapshotWriter;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.onlab.util.CountDownCompleter;
-import org.onlab.util.Match;
-import org.onosproject.store.primitives.MapUpdate;
-import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsKey;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsValue;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.EntrySet;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Get;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.GetOrDefault;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.IsEmpty;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.KeySet;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Listen;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Size;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionBegin;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionCommit;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepare;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepareAndCommit;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionRollback;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Unlisten;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.UpdateAndGet;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Values;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.TransactionLog;
-import org.onosproject.store.service.Versioned;
-import org.slf4j.Logger;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-/**
- * State Machine for {@link AtomixConsistentMap} resource.
- */
-public class AtomixConsistentMapState extends ResourceStateMachine implements SessionListener, Snapshottable {
-
- private final Logger log = getLogger(getClass());
- private final Map<Long, Commit<? extends Listen>> listeners = new HashMap<>();
- private final Map<String, MapEntryValue> mapEntries = new HashMap<>();
- private final Set<String> preparedKeys = Sets.newHashSet();
- private final Map<TransactionId, TransactionScope> activeTransactions = Maps.newHashMap();
- private long currentVersion;
-
- public AtomixConsistentMapState(Properties properties) {
- super(properties);
- }
-
- @Override
- public void snapshot(SnapshotWriter writer) {
- }
-
- @Override
- public void install(SnapshotReader reader) {
- }
-
- @Override
- protected void configure(StateMachineExecutor executor) {
- // Listeners
- executor.register(Listen.class, this::listen);
- executor.register(Unlisten.class, this::unlisten);
- // Queries
- executor.register(ContainsKey.class, this::containsKey);
- executor.register(ContainsValue.class, this::containsValue);
- executor.register(EntrySet.class, this::entrySet);
- executor.register(Get.class, this::get);
- executor.register(GetOrDefault.class, this::getOrDefault);
- executor.register(IsEmpty.class, this::isEmpty);
- executor.register(KeySet.class, this::keySet);
- executor.register(Size.class, this::size);
- executor.register(Values.class, this::values);
- // Commands
- executor.register(UpdateAndGet.class, this::updateAndGet);
- executor.register(AtomixConsistentMapCommands.Clear.class, this::clear);
- executor.register(TransactionBegin.class, this::begin);
- executor.register(TransactionPrepare.class, this::prepare);
- executor.register(TransactionCommit.class, this::commit);
- executor.register(TransactionRollback.class, this::rollback);
- executor.register(TransactionPrepareAndCommit.class, this::prepareAndCommit);
- }
-
- @Override
- public void delete() {
- // Delete Listeners
- listeners.values().forEach(Commit::close);
- listeners.clear();
-
- // Delete Map entries
- mapEntries.values().forEach(MapEntryValue::discard);
- mapEntries.clear();
- }
-
- /**
- * Handles a contains key commit.
- *
- * @param commit containsKey commit
- * @return {@code true} if map contains key
- */
- protected boolean containsKey(Commit<? extends ContainsKey> commit) {
- try {
- MapEntryValue value = mapEntries.get(commit.operation().key());
- return value != null && value.type() != MapEntryValue.Type.TOMBSTONE;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a contains value commit.
- *
- * @param commit containsValue commit
- * @return {@code true} if map contains value
- */
- protected boolean containsValue(Commit<? extends ContainsValue> commit) {
- try {
- Match<byte[]> valueMatch = Match.ifValue(commit.operation().value());
- return mapEntries.values().stream()
- .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE)
- .anyMatch(value -> valueMatch.matches(value.value()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a get commit.
- *
- * @param commit get commit
- * @return value mapped to key
- */
- protected Versioned<byte[]> get(Commit<? extends Get> commit) {
- try {
- return toVersioned(mapEntries.get(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a get or default commit.
- *
- * @param commit get or default commit
- * @return value mapped to key
- */
- protected Versioned<byte[]> getOrDefault(Commit<? extends GetOrDefault> commit) {
- try {
- MapEntryValue value = mapEntries.get(commit.operation().key());
- if (value == null) {
- return new Versioned<>(commit.operation().defaultValue(), 0);
- } else if (value.type() == MapEntryValue.Type.TOMBSTONE) {
- return new Versioned<>(commit.operation().defaultValue(), value.version);
- } else {
- return new Versioned<>(value.value(), value.version);
- }
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a count commit.
- *
- * @param commit size commit
- * @return number of entries in map
- */
- protected int size(Commit<? extends Size> commit) {
- try {
- return (int) mapEntries.values().stream()
- .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE)
- .count();
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles an is empty commit.
- *
- * @param commit isEmpty commit
- * @return {@code true} if map is empty
- */
- protected boolean isEmpty(Commit<? extends IsEmpty> commit) {
- try {
- return mapEntries.values().stream()
- .noneMatch(value -> value.type() != MapEntryValue.Type.TOMBSTONE);
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a keySet commit.
- *
- * @param commit keySet commit
- * @return set of keys in map
- */
- protected Set<String> keySet(Commit<? extends KeySet> commit) {
- try {
- return mapEntries.entrySet().stream()
- .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE)
- .map(Map.Entry::getKey)
- .collect(Collectors.toSet());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a values commit.
- *
- * @param commit values commit
- * @return collection of values in map
- */
- protected Collection<Versioned<byte[]>> values(Commit<? extends Values> commit) {
- try {
- return mapEntries.entrySet().stream()
- .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE)
- .map(entry -> toVersioned(entry.getValue()))
- .collect(Collectors.toList());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a entry set commit.
- *
- * @param commit
- * entrySet commit
- * @return set of map entries
- */
- protected Set<Map.Entry<String, Versioned<byte[]>>> entrySet(Commit<? extends EntrySet> commit) {
- try {
- return mapEntries.entrySet().stream()
- .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE)
- .map(e -> Maps.immutableEntry(e.getKey(), toVersioned(e.getValue())))
- .collect(Collectors.toSet());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a update and get commit.
- *
- * @param commit updateAndGet commit
- * @return update result
- */
- protected MapEntryUpdateResult<String, byte[]> updateAndGet(Commit<? extends UpdateAndGet> commit) {
- try {
- MapEntryUpdateResult.Status updateStatus = validate(commit.operation());
- String key = commit.operation().key();
- MapEntryValue oldCommitValue = mapEntries.get(commit.operation().key());
- Versioned<byte[]> oldMapValue = toVersioned(oldCommitValue);
-
- if (updateStatus != MapEntryUpdateResult.Status.OK) {
- commit.close();
- return new MapEntryUpdateResult<>(updateStatus, "", key, oldMapValue, oldMapValue);
- }
-
- byte[] newValue = commit.operation().value();
- currentVersion = commit.index();
- Versioned<byte[]> newMapValue = newValue == null ? null
- : new Versioned<>(newValue, currentVersion);
-
- MapEvent.Type updateType = newValue == null ? REMOVE
- : oldCommitValue == null ? INSERT : UPDATE;
-
- // If a value existed in the map, remove and discard the value to ensure disk can be freed.
- if (updateType == REMOVE || updateType == UPDATE) {
- mapEntries.remove(key);
- oldCommitValue.discard();
- }
-
- // If this is an insert/update commit, add the commit to the map entries.
- if (updateType == INSERT || updateType == UPDATE) {
- mapEntries.put(key, new NonTransactionalCommit(commit));
- } else if (!activeTransactions.isEmpty()) {
- // If this is a delete but transactions are currently running, ensure tombstones are retained
- // for version checks.
- TombstoneCommit tombstone = new TombstoneCommit(
- commit.index(),
- new CountDownCompleter<>(commit, 1, Commit::close));
- mapEntries.put(key, tombstone);
- } else {
- // If no transactions are in progress, we can safely delete the key from memory.
- commit.close();
- }
-
- publish(Lists.newArrayList(new MapEvent<>("", key, newMapValue, oldMapValue)));
- return new MapEntryUpdateResult<>(updateStatus, "", key, oldMapValue, newMapValue);
- } catch (Exception e) {
- log.error("State machine operation failed", e);
- throw Throwables.propagate(e);
- }
- }
-
- /**
- * Handles a clear commit.
- *
- * @param commit clear commit
- * @return clear result
- */
- protected MapEntryUpdateResult.Status clear(Commit<? extends Clear> commit) {
- try {
- Iterator<Map.Entry<String, MapEntryValue>> iterator = mapEntries
- .entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<String, MapEntryValue> entry = iterator.next();
- String key = entry.getKey();
- MapEntryValue value = entry.getValue();
- Versioned<byte[]> removedValue = new Versioned<>(value.value(),
- value.version());
- publish(Lists.newArrayList(new MapEvent<>("", key, null, removedValue)));
- value.discard();
- iterator.remove();
- }
- return MapEntryUpdateResult.Status.OK;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a listen commit.
- *
- * @param commit listen commit
- */
- protected void listen(Commit<? extends Listen> commit) {
- Long sessionId = commit.session().id();
- if (listeners.putIfAbsent(sessionId, commit) != null) {
- commit.close();
- return;
- }
- commit.session()
- .onStateChange(
- state -> {
- if (state == ServerSession.State.CLOSED
- || state == ServerSession.State.EXPIRED) {
- Commit<? extends Listen> listener = listeners.remove(sessionId);
- if (listener != null) {
- listener.close();
- }
- }
- });
- }
-
- /**
- * Handles an unlisten commit.
- *
- * @param commit unlisten commit
- */
- protected void unlisten(Commit<? extends Unlisten> commit) {
- try {
- Commit<? extends Listen> listener = listeners.remove(commit.session().id());
- if (listener != null) {
- listener.close();
- }
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a begin commit.
- *
- * @param commit transaction begin commit
- * @return transaction state version
- */
- protected long begin(Commit<? extends TransactionBegin> commit) {
- try {
- long version = commit.index();
- activeTransactions.put(commit.operation().transactionId(), new TransactionScope(version));
- return version;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles an prepare and commit commit.
- *
- * @param commit transaction prepare and commit commit
- * @return prepare result
- */
- protected PrepareResult prepareAndCommit(Commit<? extends TransactionPrepareAndCommit> commit) {
- TransactionId transactionId = commit.operation().transactionLog().transactionId();
- PrepareResult prepareResult = prepare(commit);
- TransactionScope transactionScope = activeTransactions.remove(transactionId);
- if (prepareResult == PrepareResult.OK) {
- this.currentVersion = commit.index();
- transactionScope = transactionScope.prepared(commit);
- commit(transactionScope);
- } else if (transactionScope != null) {
- transactionScope.close();
- }
- discardTombstones();
- return prepareResult;
- }
-
- /**
- * Handles an prepare commit.
- *
- * @param commit transaction prepare commit
- * @return prepare result
- */
- protected PrepareResult prepare(Commit<? extends TransactionPrepare> commit) {
- boolean ok = false;
-
- try {
- TransactionLog<MapUpdate<String, byte[]>> transactionLog = commit.operation().transactionLog();
-
- // Iterate through records in the transaction log and perform isolation checks.
- for (MapUpdate<String, byte[]> record : transactionLog.records()) {
- String key = record.key();
-
- // If the record is a VERSION_MATCH then check that the record's version matches the current
- // version of the state machine.
- if (record.type() == MapUpdate.Type.VERSION_MATCH && key == null) {
- if (record.version() > currentVersion) {
- return PrepareResult.OPTIMISTIC_LOCK_FAILURE;
- } else {
- continue;
- }
- }
-
- // If the prepared keys already contains the key contained within the record, that indicates a
- // conflict with a concurrent transaction.
- if (preparedKeys.contains(key)) {
- return PrepareResult.CONCURRENT_TRANSACTION;
- }
-
- // Read the existing value from the map.
- MapEntryValue existingValue = mapEntries.get(key);
-
- // Note: if the existing value is null, that means the key has not changed during the transaction,
- // otherwise a tombstone would have been retained.
- if (existingValue == null) {
- // If the value is null, ensure the version is equal to the transaction version.
- if (record.version() != transactionLog.version()) {
- return PrepareResult.OPTIMISTIC_LOCK_FAILURE;
- }
- } else {
- // If the value is non-null, compare the current version with the record version.
- if (existingValue.version() > record.version()) {
- return PrepareResult.OPTIMISTIC_LOCK_FAILURE;
- }
- }
- }
-
- // No violations detected. Mark modified keys locked for transactions.
- transactionLog.records().forEach(record -> {
- if (record.type() != MapUpdate.Type.VERSION_MATCH) {
- preparedKeys.add(record.key());
- }
- });
-
- ok = true;
-
- // Update the transaction scope. If the transaction scope is not set on this node, that indicates the
- // coordinator is communicating with another node. Transactions assume that the client is communicating
- // with a single leader in order to limit the overhead of retaining tombstones.
- TransactionScope transactionScope = activeTransactions.get(transactionLog.transactionId());
- if (transactionScope == null) {
- activeTransactions.put(
- transactionLog.transactionId(),
- new TransactionScope(transactionLog.version(), commit));
- return PrepareResult.PARTIAL_FAILURE;
- } else {
- activeTransactions.put(
- transactionLog.transactionId(),
- transactionScope.prepared(commit));
- return PrepareResult.OK;
- }
- } catch (Exception e) {
- log.warn("Failure applying {}", commit, e);
- throw Throwables.propagate(e);
- } finally {
- if (!ok) {
- commit.close();
- }
- }
- }
-
- /**
- * Handles an commit commit (ha!).
- *
- * @param commit transaction commit commit
- * @return commit result
- */
- protected CommitResult commit(Commit<? extends TransactionCommit> commit) {
- TransactionId transactionId = commit.operation().transactionId();
- TransactionScope transactionScope = activeTransactions.remove(transactionId);
- if (transactionScope == null) {
- return CommitResult.UNKNOWN_TRANSACTION_ID;
- }
-
- try {
- this.currentVersion = commit.index();
- return commit(transactionScope.committed(commit));
- } catch (Exception e) {
- log.warn("Failure applying {}", commit, e);
- throw Throwables.propagate(e);
- } finally {
- discardTombstones();
- }
- }
-
- /**
- * Applies committed operations to the state machine.
- */
- private CommitResult commit(TransactionScope transactionScope) {
- TransactionLog<MapUpdate<String, byte[]>> transactionLog = transactionScope.transactionLog();
- boolean retainTombstones = !activeTransactions.isEmpty();
-
- // Count the total number of keys that will be set by this transaction. This is necessary to do reference
- // counting for garbage collection.
- long totalReferencesToCommit = transactionLog.records().stream()
- // No keys are set for version checks. For deletes, references are only retained of tombstones
- // need to be retained for concurrent transactions.
- .filter(record -> record.type() != MapUpdate.Type.VERSION_MATCH && record.type() != MapUpdate.Type.LOCK
- && (record.type() != MapUpdate.Type.REMOVE_IF_VERSION_MATCH || retainTombstones))
- .count();
-
- // Create a count down completer that counts references to the transaction commit for garbage collection.
- CountDownCompleter<TransactionScope> completer = new CountDownCompleter<>(
- transactionScope, totalReferencesToCommit, TransactionScope::close);
-
- List<MapEvent<String, byte[]>> eventsToPublish = Lists.newArrayList();
- for (MapUpdate<String, byte[]> record : transactionLog.records()) {
- if (record.type() == MapUpdate.Type.VERSION_MATCH) {
- continue;
- }
-
- String key = record.key();
- checkState(preparedKeys.remove(key), "key is not prepared");
-
- if (record.type() == MapUpdate.Type.LOCK) {
- continue;
- }
-
- MapEntryValue previousValue = mapEntries.remove(key);
- MapEntryValue newValue = null;
-
- // If the record is not a delete, create a transactional commit.
- if (record.type() != MapUpdate.Type.REMOVE_IF_VERSION_MATCH) {
- newValue = new TransactionalCommit(currentVersion, record.value(), completer);
- } else if (retainTombstones) {
- // For deletes, if tombstones need to be retained then create and store a tombstone commit.
- newValue = new TombstoneCommit(currentVersion, completer);
- }
-
- eventsToPublish.add(new MapEvent<>("", key, toVersioned(newValue), toVersioned(previousValue)));
-
- if (newValue != null) {
- mapEntries.put(key, newValue);
- }
-
- if (previousValue != null) {
- previousValue.discard();
- }
- }
- publish(eventsToPublish);
- return CommitResult.OK;
- }
-
- /**
- * Handles an rollback commit (ha!).
- *
- * @param commit transaction rollback commit
- * @return rollback result
- */
- protected RollbackResult rollback(Commit<? extends TransactionRollback> commit) {
- TransactionId transactionId = commit.operation().transactionId();
- TransactionScope transactionScope = activeTransactions.remove(transactionId);
- if (transactionScope == null) {
- return RollbackResult.UNKNOWN_TRANSACTION_ID;
- } else if (!transactionScope.isPrepared()) {
- discardTombstones();
- transactionScope.close();
- commit.close();
- return RollbackResult.OK;
- } else {
- try {
- transactionScope.transactionLog().records()
- .forEach(record -> {
- if (record.type() != MapUpdate.Type.VERSION_MATCH) {
- preparedKeys.remove(record.key());
- }
- });
- return RollbackResult.OK;
- } finally {
- discardTombstones();
- transactionScope.close();
- commit.close();
- }
- }
-
- }
-
- /**
- * Discards tombstones no longer needed by active transactions.
- */
- private void discardTombstones() {
- if (activeTransactions.isEmpty()) {
- Iterator<Map.Entry<String, MapEntryValue>> iterator = mapEntries.entrySet().iterator();
- while (iterator.hasNext()) {
- MapEntryValue value = iterator.next().getValue();
- if (value.type() == MapEntryValue.Type.TOMBSTONE) {
- iterator.remove();
- value.discard();
- }
- }
- } else {
- long lowWaterMark = activeTransactions.values().stream()
- .mapToLong(TransactionScope::version)
- .min().getAsLong();
- Iterator<Map.Entry<String, MapEntryValue>> iterator = mapEntries.entrySet().iterator();
- while (iterator.hasNext()) {
- MapEntryValue value = iterator.next().getValue();
- if (value.type() == MapEntryValue.Type.TOMBSTONE && value.version < lowWaterMark) {
- iterator.remove();
- value.discard();
- }
- }
- }
- }
-
- /**
- * Computes the update status that would result if the specified update were to applied to
- * the state machine.
- *
- * @param update update
- * @return status
- */
- private MapEntryUpdateResult.Status validate(UpdateAndGet update) {
- MapEntryValue existingValue = mapEntries.get(update.key());
- boolean isEmpty = existingValue == null || existingValue.type() == MapEntryValue.Type.TOMBSTONE;
- if (isEmpty && update.value() == null) {
- return MapEntryUpdateResult.Status.NOOP;
- }
- if (preparedKeys.contains(update.key())) {
- return MapEntryUpdateResult.Status.WRITE_LOCK;
- }
- byte[] existingRawValue = isEmpty ? null : existingValue.value();
- Long existingVersion = isEmpty ? null : existingValue.version();
- return update.valueMatch().matches(existingRawValue)
- && update.versionMatch().matches(existingVersion) ? MapEntryUpdateResult.Status.OK
- : MapEntryUpdateResult.Status.PRECONDITION_FAILED;
- }
-
- /**
- * Utility for turning a {@code MapEntryValue} to {@code Versioned}.
- * @param value map entry value
- * @return versioned instance
- */
- private Versioned<byte[]> toVersioned(MapEntryValue value) {
- return value != null && value.type() != MapEntryValue.Type.TOMBSTONE
- ? new Versioned<>(value.value(), value.version()) : null;
- }
-
- /**
- * Publishes events to listeners.
- *
- * @param events list of map event to publish
- */
- private void publish(List<MapEvent<String, byte[]>> events) {
- listeners.values().forEach(commit -> commit.session().publish(AtomixConsistentMap.CHANGE_SUBJECT, events));
- }
-
- @Override
- public void register(ServerSession session) {
- }
-
- @Override
- public void unregister(ServerSession session) {
- closeListener(session.id());
- }
-
- @Override
- public void expire(ServerSession session) {
- closeListener(session.id());
- }
-
- @Override
- public void close(ServerSession session) {
- closeListener(session.id());
- }
-
- private void closeListener(Long sessionId) {
- Commit<? extends Listen> commit = listeners.remove(sessionId);
- if (commit != null) {
- commit.close();
- }
- }
-
- /**
- * Interface implemented by map values.
- */
- private abstract static class MapEntryValue {
- protected final Type type;
- protected final long version;
-
- MapEntryValue(Type type, long version) {
- this.type = type;
- this.version = version;
- }
-
- /**
- * Returns the value type.
- *
- * @return the value type
- */
- Type type() {
- return type;
- }
-
- /**
- * Returns the version of the value.
- *
- * @return version
- */
- long version() {
- return version;
- }
-
- /**
- * Returns the raw {@code byte[]}.
- *
- * @return raw value
- */
- abstract byte[] value();
-
- /**
- * Discards the value by invoke appropriate clean up actions.
- */
- abstract void discard();
-
- /**
- * Value type.
- */
- enum Type {
- VALUE,
- TOMBSTONE,
- }
- }
-
- /**
- * A {@code MapEntryValue} that is derived from a non-transactional update
- * i.e. via any standard map update operation.
- */
- private static class NonTransactionalCommit extends MapEntryValue {
- private final Commit<? extends UpdateAndGet> commit;
-
- NonTransactionalCommit(Commit<? extends UpdateAndGet> commit) {
- super(Type.VALUE, commit.index());
- this.commit = commit;
- }
-
- @Override
- byte[] value() {
- return commit.operation().value();
- }
-
- @Override
- void discard() {
- commit.close();
- }
- }
-
- /**
- * A {@code MapEntryValue} that is derived from updates submitted via a
- * transaction.
- */
- private static class TransactionalCommit extends MapEntryValue {
- private final byte[] value;
- private final CountDownCompleter<?> completer;
-
- TransactionalCommit(long version, byte[] value, CountDownCompleter<?> completer) {
- super(Type.VALUE, version);
- this.value = value;
- this.completer = completer;
- }
-
- @Override
- byte[] value() {
- return value;
- }
-
- @Override
- void discard() {
- completer.countDown();
- }
- }
-
- /**
- * A {@code MapEntryValue} that represents a deleted entry.
- */
- private static class TombstoneCommit extends MapEntryValue {
- private final CountDownCompleter<?> completer;
-
- public TombstoneCommit(long version, CountDownCompleter<?> completer) {
- super(Type.TOMBSTONE, version);
- this.completer = completer;
- }
-
- @Override
- byte[] value() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- void discard() {
- completer.countDown();
- }
- }
-
- /**
- * Map transaction scope.
- */
- private static final class TransactionScope {
- private final long version;
- private final Commit<? extends TransactionPrepare> prepareCommit;
- private final Commit<? extends TransactionCommit> commitCommit;
-
- private TransactionScope(long version) {
- this(version, null, null);
- }
-
- private TransactionScope(
- long version,
- Commit<? extends TransactionPrepare> prepareCommit) {
- this(version, prepareCommit, null);
- }
-
- private TransactionScope(
- long version,
- Commit<? extends TransactionPrepare> prepareCommit,
- Commit<? extends TransactionCommit> commitCommit) {
- this.version = version;
- this.prepareCommit = prepareCommit;
- this.commitCommit = commitCommit;
- }
-
- /**
- * Returns the transaction version.
- *
- * @return the transaction version
- */
- long version() {
- return version;
- }
-
- /**
- * Returns whether this is a prepared transaction scope.
- *
- * @return whether this is a prepared transaction scope
- */
- boolean isPrepared() {
- return prepareCommit != null;
- }
-
- /**
- * Returns the transaction commit log.
- *
- * @return the transaction commit log
- */
- TransactionLog<MapUpdate<String, byte[]>> transactionLog() {
- checkState(isPrepared());
- return prepareCommit.operation().transactionLog();
- }
-
- /**
- * Returns a new transaction scope with a prepare commit.
- *
- * @param commit the prepare commit
- * @return new transaction scope updated with the prepare commit
- */
- TransactionScope prepared(Commit<? extends TransactionPrepare> commit) {
- return new TransactionScope(version, commit);
- }
-
- /**
- * Returns a new transaction scope with a commit commit.
- *
- * @param commit the commit commit ;-)
- * @return new transaction scope updated with the commit commit
- */
- TransactionScope committed(Commit<? extends TransactionCommit> commit) {
- checkState(isPrepared());
- return new TransactionScope(version, prepareCommit, commit);
- }
-
- /**
- * Closes the transaction and all associated commits.
- */
- void close() {
- if (prepareCommit != null) {
- prepareCommit.close();
- }
- if (commitCommit != null) {
- commitCommit.close();
- }
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java
deleted file mode 100644
index b8e5d4c..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Multiset;
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.catalyst.util.Assert;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-import org.onlab.util.Match;
-import org.onosproject.store.service.Versioned;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * AsyncConsistentMultimap state machine commands.
- */
-public final class AtomixConsistentMultimapCommands {
-
- private AtomixConsistentMultimapCommands() {
- }
-
- /**
- * Abstract multimap command.
- */
- @SuppressWarnings("serial")
- public abstract static class MultimapCommand<V> implements Command<V>,
- CatalystSerializable {
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- /**
- * Abstract multimap query.
- */
- @SuppressWarnings("serial")
- public abstract static class MultimapQuery<V> implements Query<V>,
- CatalystSerializable {
- @Override
- public ConsistencyLevel consistency() {
- return ConsistencyLevel.SEQUENTIAL;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer,
- Serializer serializer) {
- }
- }
-
- /**
- * Abstract key-based multimap query.
- */
- @SuppressWarnings("serial")
- public abstract static class KeyQuery<V> extends MultimapQuery<V> {
- protected String key;
-
- public KeyQuery() {
- }
-
- public KeyQuery(String key) {
- this.key = Assert.notNull(key, "key");
- }
-
- public String key() {
- return key;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- }
- }
-
- /**
- * Abstract value-based query.
- */
- @SuppressWarnings("serial")
- public abstract static class ValueQuery<V> extends MultimapQuery<V> {
- protected byte[] value;
-
- public ValueQuery() {
- }
-
- public ValueQuery(byte[] value) {
- this.value = Assert.notNull(value, "value");
- }
-
- /**
- * Returns the value.
- *
- * @return value.
- */
- public byte[] value() {
- return value;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("value", value)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(value, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- value = serializer.readObject(buffer);
- }
- }
-
- /**
- * Size query.
- */
- public static class Size extends MultimapQuery<Integer> {
- }
-
- /**
- * Is empty query.
- */
- public static class IsEmpty extends MultimapQuery<Boolean> {
- }
-
- /**
- * Contains key query.
- */
- @SuppressWarnings("serial")
- public static class ContainsKey extends KeyQuery<Boolean> {
- public ContainsKey() {
- }
-
- public ContainsKey(String key) {
- super(key);
- }
-
- }
-
- /**
- * Contains value query.
- */
- @SuppressWarnings("serial")
- public static class ContainsValue extends ValueQuery<Boolean> {
- public ContainsValue() {
- }
-
- public ContainsValue(byte[] value) {
- super(value);
- }
- }
-
- /**
- * Contains entry query.
- */
- @SuppressWarnings("serial")
- public static class ContainsEntry extends MultimapQuery<Boolean> {
- protected String key;
- protected byte[] value;
-
- public ContainsEntry() {
- }
-
- public ContainsEntry(String key, byte[] value) {
- this.key = Assert.notNull(key, "key");
- this.value = Assert.notNull(value, "value");
- }
-
- public String key() {
- return key;
- }
-
- public byte[] value() {
- return value;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("value", value)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(value, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- value = serializer.readObject(buffer);
-
- }
- }
-
- /**
- * Remove command, backs remove and removeAll's that return booleans.
- */
- @SuppressWarnings("serial")
- public static class RemoveAll extends
- MultimapCommand<Versioned<Collection<? extends byte[]>>> {
- private String key;
- private Match<Long> versionMatch;
-
- public RemoveAll() {
- }
-
- public RemoveAll(String key, Match<Long> versionMatch) {
- this.key = Assert.notNull(key, "key");
- this.versionMatch = versionMatch;
- }
-
- public String key() {
- return this.key;
- }
-
- public Match<Long> versionMatch() {
- return versionMatch;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Remove command, backs remove and removeAll's that return booleans.
- */
- @SuppressWarnings("serial")
- public static class MultiRemove extends
- MultimapCommand<Boolean> {
- private String key;
- private Collection<byte[]> values;
- private Match<Long> versionMatch;
-
- public MultiRemove() {
- }
-
- public MultiRemove(String key, Collection<byte[]> valueMatches,
- Match<Long> versionMatch) {
- this.key = Assert.notNull(key, "key");
- this.values = valueMatches;
- this.versionMatch = versionMatch;
- }
-
- public String key() {
- return this.key;
- }
-
- public Collection<byte[]> values() {
- return values;
- }
-
- public Match<Long> versionMatch() {
- return versionMatch;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(values, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- values = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("values", values)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Command to back the put and putAll methods.
- */
- @SuppressWarnings("serial")
- public static class Put extends MultimapCommand<Boolean> {
- private String key;
- private Collection<? extends byte[]> values;
- private Match<Long> versionMatch;
-
- public Put() {
- }
-
- public Put(String key, Collection<? extends byte[]> values,
- Match<Long> versionMatch) {
- this.key = Assert.notNull(key, "key");
- this.values = values;
- this.versionMatch = versionMatch;
- }
-
- public String key() {
- return key;
- }
-
- public Collection<? extends byte[]> values() {
- return values;
- }
-
- public Match<Long> versionMatch() {
- return versionMatch;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(values, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- values = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("values", values)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Replace command, returns the collection that was replaced.
- */
- @SuppressWarnings("serial")
- public static class Replace extends
- MultimapCommand<Versioned<Collection<? extends byte[]>>> {
- private String key;
- private Collection<byte[]> values;
- private Match<Long> versionMatch;
-
- public Replace() {
- }
-
- public Replace(String key, Collection<byte[]> values,
- Match<Long> versionMatch) {
- this.key = Assert.notNull(key, "key");
- this.values = values;
- this.versionMatch = versionMatch;
- }
-
- public String key() {
- return this.key;
- }
-
- public Match<Long> versionMatch() {
- return versionMatch;
- }
-
- public Collection<byte[]> values() {
- return values;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(values, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- values = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("values", values)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Clear multimap command.
- */
- @SuppressWarnings("serial")
- public static class Clear extends MultimapCommand<Void> {
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Key set query.
- */
- @SuppressWarnings("serial")
- public static class KeySet extends MultimapQuery<Set<String>> {
- }
-
- /**
- * Key multiset query.
- */
- @SuppressWarnings("serial")
- public static class Keys extends MultimapQuery<Multiset<String>> {
- }
-
- /**
- * Value collection query.
- */
- @SuppressWarnings("serial")
- public static class Values extends MultimapQuery<Multiset<byte[]>> {
- }
-
- /**
- * Entry set query.
- */
- @SuppressWarnings("serial")
- public static class Entries extends
- MultimapQuery<Collection<Map.Entry<String, byte[]>>> {
- }
-
- /**
- * Get value query.
- */
- public static class Get extends
- KeyQuery<Versioned<Collection<? extends byte[]>>> {
- public Get() {
- }
-
- public Get(String key) {
- super(key);
- }
- }
-
- /**
- * Change listen.
- */
- @SuppressWarnings("serial")
- public static class Listen implements Command<Void>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
- }
-
- /**
- * Change unlisten.
- */
- @SuppressWarnings("serial")
- public static class Unlisten implements Command<Void>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Multimap command type resolver.
- */
- @SuppressWarnings("serial")
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- registry.register(ContainsKey.class, -1000);
- registry.register(ContainsValue.class, -1001);
- registry.register(ContainsEntry.class, -1002);
- registry.register(Replace.class, -1003);
- registry.register(Clear.class, -1004);
- registry.register(KeySet.class, -1005);
- registry.register(Keys.class, -1006);
- registry.register(Values.class, -1007);
- registry.register(Entries.class, -1008);
- registry.register(Size.class, -1009);
- registry.register(IsEmpty.class, -1010);
- registry.register(Get.class, -1011);
- registry.register(Put.class, -1012);
- registry.register(RemoveAll.class, -1013);
- registry.register(MultiRemove.class, -1014);
- registry.register(Listen.class, -1015);
- registry.register(Unlisten.class, -1016);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java
index 22a0345..a9ea68b 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java
@@ -16,43 +16,52 @@
package org.onosproject.store.primitives.resources.impl;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multiset;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-import org.onosproject.store.service.AsyncConsistentMultimap;
-import org.onosproject.store.service.MultimapEvent;
-import org.onosproject.store.service.MultimapEventListener;
-import org.onosproject.store.service.Versioned;
-
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Clear;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsValue;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Entries;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Get;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.IsEmpty;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.KeySet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Keys;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Listen;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.MultiRemove;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Put;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.RemoveAll;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Replace;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Size;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Unlisten;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Values;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multiset;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.AsyncConsistentMultimap;
+import org.onosproject.store.service.MultimapEvent;
+import org.onosproject.store.service.MultimapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Versioned;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsValue;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ENTRIES;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Get;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEYS;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.MultiRemove;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.PUT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Put;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_ALL;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REPLACE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.RemoveAll;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Replace;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.SIZE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.VALUES;
/**
@@ -60,30 +69,25 @@
* <p>
* Note: this implementation does not allow null entries or duplicate entries.
*/
-@ResourceTypeInfo(id = -153, factory = AtomixConsistentSetMultimapFactory.class)
public class AtomixConsistentSetMultimap
- extends AbstractResource<AtomixConsistentSetMultimap>
+ extends AbstractRaftPrimitive
implements AsyncConsistentMultimap<String, byte[]> {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixConsistentSetMultimapOperations.NAMESPACE)
+ .register(AtomixConsistentSetMultimapEvents.NAMESPACE)
+ .build());
+
private final Map<MultimapEventListener<String, byte[]>, Executor> mapEventListeners = new ConcurrentHashMap<>();
- public static final String CHANGE_SUBJECT = "multimapChangeEvents";
-
- public AtomixConsistentSetMultimap(CopycatClient client,
- Properties properties) {
- super(client, properties);
- }
-
- @Override
- public CompletableFuture<AtomixConsistentSetMultimap> open() {
- return super.open().thenApply(result -> {
- client.onStateChange(state -> {
- if (state == CopycatClient.State.CONNECTED && isListening()) {
- client.submit(new Listen());
- }
- });
- client.onEvent(CHANGE_SUBJECT, this::handleEvent);
- return result;
+ public AtomixConsistentSetMultimap(RaftProxy proxy) {
+ super(proxy);
+ proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent);
+ proxy.addStateChangeListener(state -> {
+ if (state == RaftProxy.State.CONNECTED && isListening()) {
+ proxy.invoke(ADD_LISTENER);
+ }
});
}
@@ -94,97 +98,109 @@
@Override
public CompletableFuture<Integer> size() {
- return client.submit(new Size());
+ return proxy.invoke(SIZE, SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> isEmpty() {
- return client.submit(new IsEmpty());
+ return proxy.invoke(IS_EMPTY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsKey(String key) {
- return client.submit(new ContainsKey(key));
+ return proxy.invoke(CONTAINS_KEY, SERIALIZER::encode, new ContainsKey(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsValue(byte[] value) {
- return client.submit(new ContainsValue(value));
+ return proxy.invoke(CONTAINS_VALUE, SERIALIZER::encode, new ContainsValue(value), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsEntry(String key, byte[] value) {
- return client.submit(new ContainsEntry(key, value));
+ return proxy.invoke(CONTAINS_ENTRY, SERIALIZER::encode, new ContainsEntry(key, value), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> put(String key, byte[] value) {
- return client.submit(new Put(key, Lists.newArrayList(value), null));
+ return proxy.invoke(
+ PUT,
+ SERIALIZER::encode,
+ new Put(key, Lists.newArrayList(value), null),
+ SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> remove(String key, byte[] value) {
- return client.submit(new MultiRemove(key,
- Lists.newArrayList(value),
- null));
+ return proxy.invoke(REMOVE, SERIALIZER::encode, new MultiRemove(key,
+ Lists.newArrayList(value),
+ null), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> removeAll(String key, Collection<? extends byte[]> values) {
- return client.submit(new MultiRemove(key, (Collection<byte[]>) values, null));
+ return proxy.invoke(
+ REMOVE,
+ SERIALIZER::encode,
+ new MultiRemove(key, (Collection<byte[]>) values, null),
+ SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<Collection<? extends byte[]>>> removeAll(String key) {
- return client.submit(new RemoveAll(key, null));
+ return proxy.invoke(REMOVE_ALL, SERIALIZER::encode, new RemoveAll(key, null), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> putAll(
String key, Collection<? extends byte[]> values) {
- return client.submit(new Put(key, values, null));
+ return proxy.invoke(PUT, SERIALIZER::encode, new Put(key, values, null), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<Collection<? extends byte[]>>> replaceValues(
String key, Collection<byte[]> values) {
- return client.submit(new Replace(key, values, null));
+ return proxy.invoke(
+ REPLACE,
+ SERIALIZER::encode,
+ new Replace(key, values, null),
+ SERIALIZER::decode);
}
@Override
public CompletableFuture<Void> clear() {
- return client.submit(new Clear());
+ return proxy.invoke(CLEAR);
}
@Override
public CompletableFuture<Versioned<Collection<? extends byte[]>>> get(String key) {
- return client.submit(new Get(key));
+ return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Set<String>> keySet() {
- return client.submit(new KeySet());
+ return proxy.invoke(KEY_SET, SERIALIZER::decode);
}
@Override
public CompletableFuture<Multiset<String>> keys() {
- return client.submit(new Keys());
+ return proxy.invoke(KEYS, SERIALIZER::decode);
}
@Override
public CompletableFuture<Multiset<byte[]>> values() {
- return client.submit(new Values());
+ return proxy.invoke(VALUES, SERIALIZER::decode);
}
@Override
public CompletableFuture<Collection<Map.Entry<String, byte[]>>> entries() {
- return client.submit(new Entries());
+ return proxy.invoke(ENTRIES, SERIALIZER::decode);
}
@Override
public CompletableFuture<Void> addListener(MultimapEventListener<String, byte[]> listener, Executor executor) {
if (mapEventListeners.isEmpty()) {
- return client.submit(new Listen()).thenRun(() -> mapEventListeners.put(listener, executor));
+ return proxy.invoke(ADD_LISTENER).thenRun(() -> mapEventListeners.put(listener, executor));
} else {
mapEventListeners.put(listener, executor);
return CompletableFuture.completedFuture(null);
@@ -194,7 +210,7 @@
@Override
public CompletableFuture<Void> removeListener(MultimapEventListener<String, byte[]> listener) {
if (mapEventListeners.remove(listener) != null && mapEventListeners.isEmpty()) {
- return client.submit(new Unlisten()).thenApply(v -> null);
+ return proxy.invoke(REMOVE_LISTENER).thenApply(v -> null);
}
return CompletableFuture.completedFuture(null);
}
@@ -204,11 +220,6 @@
throw new UnsupportedOperationException("Expensive operation.");
}
- @Override
- public String name() {
- return null;
- }
-
/**
* Helper to check if there was a lock based issue.
* @param status the status of an update result
@@ -216,12 +227,12 @@
private void throwIfLocked(MapEntryUpdateResult.Status status) {
if (status == MapEntryUpdateResult.Status.WRITE_LOCK) {
throw new ConcurrentModificationException("Cannot update map: " +
- "Another transaction " +
- "in progress");
+ "Another transaction " +
+ "in progress");
}
}
private boolean isListening() {
return !mapEventListeners.isEmpty();
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java
new file mode 100644
index 0000000..6c71915
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.event.EventType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.MultimapEvent;
+
+/**
+ * Atomix consistent set multimap events.
+ */
+public enum AtomixConsistentSetMultimapEvents implements EventType {
+ CHANGE("change");
+
+ private final String id;
+
+ AtomixConsistentSetMultimapEvents(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50)
+ .register(MultimapEvent.class)
+ .register(MultimapEvent.Type.class)
+ .register(byte[].class)
+ .build("AtomixConsistentSetMultimapEvents");
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java
deleted file mode 100644
index e6ebbda..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * {@link AtomixConsistentSetMultimap} resource factory.
- */
-public class AtomixConsistentSetMultimapFactory implements
- ResourceFactory<AtomixConsistentSetMultimap> {
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixConsistentMultimapCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixConsistentSetMultimapState(config);
- }
-
- @Override
- public AtomixConsistentSetMultimap createInstance(CopycatClient client,
- Properties properties) {
- return new AtomixConsistentSetMultimap(client, properties);
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java
new file mode 100644
index 0000000..ddbbc5b
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Maps;
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Versioned;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * AsyncConsistentMultimap state machine commands.
+ */
+public enum AtomixConsistentSetMultimapOperations implements OperationId {
+ GET("get", OperationType.QUERY),
+ SIZE("size", OperationType.QUERY),
+ IS_EMPTY("isEmpty", OperationType.QUERY),
+ CONTAINS_KEY("containsKey", OperationType.QUERY),
+ CONTAINS_VALUE("containsValue", OperationType.QUERY),
+ CONTAINS_ENTRY("containsEntry", OperationType.QUERY),
+ KEY_SET("keySet", OperationType.QUERY),
+ KEYS("keys", OperationType.QUERY),
+ VALUES("values", OperationType.QUERY),
+ ENTRIES("entries", OperationType.QUERY),
+ PUT("put", OperationType.COMMAND),
+ REMOVE("remove", OperationType.COMMAND),
+ REMOVE_ALL("removeAll", OperationType.COMMAND),
+ REPLACE("replace", OperationType.COMMAND),
+ CLEAR("clear", OperationType.COMMAND),
+ ADD_LISTENER("addListener", OperationType.COMMAND),
+ REMOVE_LISTENER("removeListener", OperationType.COMMAND);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixConsistentSetMultimapOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(ContainsEntry.class)
+ .register(ContainsKey.class)
+ .register(ContainsValue.class)
+ .register(Get.class)
+ .register(MultiRemove.class)
+ .register(Put.class)
+ .register(RemoveAll.class)
+ .register(Replace.class)
+ .register(Match.class)
+ .register(Versioned.class)
+ .register(ArrayList.class)
+ .register(Maps.immutableEntry("", "").getClass())
+ .build("AtomixConsistentSetMultimapOperations");
+
+ /**
+ * Abstract multimap command.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class MultimapOperation {
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .toString();
+ }
+ }
+
+ /**
+ * Abstract key-based multimap query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class KeyOperation extends MultimapOperation {
+ protected String key;
+
+ public KeyOperation() {
+ }
+
+ public KeyOperation(String key) {
+ this.key = checkNotNull(key);
+ }
+
+ public String key() {
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .toString();
+ }
+ }
+
+ /**
+ * Abstract value-based query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class ValueOperation extends MultimapOperation {
+ protected byte[] value;
+
+ public ValueOperation() {
+ }
+
+ public ValueOperation(byte[] value) {
+ this.value = checkNotNull(value);
+ }
+
+ /**
+ * Returns the value.
+ *
+ * @return value.
+ */
+ public byte[] value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("value", value)
+ .toString();
+ }
+ }
+
+ /**
+ * Contains key query.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsKey extends KeyOperation {
+ public ContainsKey() {
+ }
+
+ public ContainsKey(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Contains value query.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsValue extends ValueOperation {
+ public ContainsValue() {
+ }
+
+ public ContainsValue(byte[] value) {
+ super(value);
+ }
+ }
+
+ /**
+ * Contains entry query.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsEntry extends MultimapOperation {
+ protected String key;
+ protected byte[] value;
+
+ public ContainsEntry() {
+ }
+
+ public ContainsEntry(String key, byte[] value) {
+ this.key = checkNotNull(key);
+ this.value = checkNotNull(value);
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public byte[] value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("value", value)
+ .toString();
+ }
+ }
+
+ /**
+ * Remove command, backs remove and removeAll's that return booleans.
+ */
+ @SuppressWarnings("serial")
+ public static class RemoveAll extends MultimapOperation {
+ private String key;
+ private Match<Long> versionMatch;
+
+ public RemoveAll() {
+ }
+
+ public RemoveAll(String key, Match<Long> versionMatch) {
+ this.key = checkNotNull(key);
+ this.versionMatch = versionMatch;
+ }
+
+ public String key() {
+ return this.key;
+ }
+
+ public Match<Long> versionMatch() {
+ return versionMatch;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Remove command, backs remove and removeAll's that return booleans.
+ */
+ @SuppressWarnings("serial")
+ public static class MultiRemove extends MultimapOperation {
+ private String key;
+ private Collection<byte[]> values;
+ private Match<Long> versionMatch;
+
+ public MultiRemove() {
+ }
+
+ public MultiRemove(String key, Collection<byte[]> valueMatches,
+ Match<Long> versionMatch) {
+ this.key = checkNotNull(key);
+ this.values = valueMatches;
+ this.versionMatch = versionMatch;
+ }
+
+ public String key() {
+ return this.key;
+ }
+
+ public Collection<byte[]> values() {
+ return values;
+ }
+
+ public Match<Long> versionMatch() {
+ return versionMatch;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("values", values)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Command to back the put and putAll methods.
+ */
+ @SuppressWarnings("serial")
+ public static class Put extends MultimapOperation {
+ private String key;
+ private Collection<? extends byte[]> values;
+ private Match<Long> versionMatch;
+
+ public Put() {
+ }
+
+ public Put(String key, Collection<? extends byte[]> values, Match<Long> versionMatch) {
+ this.key = checkNotNull(key);
+ this.values = values;
+ this.versionMatch = versionMatch;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public Collection<? extends byte[]> values() {
+ return values;
+ }
+
+ public Match<Long> versionMatch() {
+ return versionMatch;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("values", values)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Replace command, returns the collection that was replaced.
+ */
+ @SuppressWarnings("serial")
+ public static class Replace extends MultimapOperation {
+ private String key;
+ private Collection<byte[]> values;
+ private Match<Long> versionMatch;
+
+ public Replace() {
+ }
+
+ public Replace(String key, Collection<byte[]> values,
+ Match<Long> versionMatch) {
+ this.key = checkNotNull(key);
+ this.values = values;
+ this.versionMatch = versionMatch;
+ }
+
+ public String key() {
+ return this.key;
+ }
+
+ public Match<Long> versionMatch() {
+ return versionMatch;
+ }
+
+ public Collection<byte[]> values() {
+ return values;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("values", values)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Get value query.
+ */
+ public static class Get extends KeyOperation {
+ public Get() {
+ }
+
+ public Get(String key) {
+ super(key);
+ }
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapService.java
new file mode 100644
index 0000000..3029bd8
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapService.java
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiConsumer;
+import java.util.function.BinaryOperator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.session.RaftSession;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.MultimapEvent;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Versioned;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsValue;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ENTRIES;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Get;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEYS;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.MultiRemove;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.MultimapOperation;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.PUT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Put;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_ALL;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REPLACE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.RemoveAll;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Replace;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.SIZE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.VALUES;
+
+/**
+ * State Machine for {@link AtomixConsistentSetMultimap} resource.
+ */
+public class AtomixConsistentSetMultimapService extends AbstractRaftService {
+
+ private final Serializer serializer = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixConsistentSetMultimapOperations.NAMESPACE)
+ .register(AtomixConsistentSetMultimapEvents.NAMESPACE)
+ .register(ByteArrayComparator.class)
+ .register(new HashMap().keySet().getClass())
+ .register(TreeSet.class)
+ .register(new com.esotericsoftware.kryo.Serializer<NonTransactionalCommit>() {
+ @Override
+ public void write(Kryo kryo, Output output, NonTransactionalCommit object) {
+ kryo.writeClassAndObject(output, object.valueSet);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public NonTransactionalCommit read(Kryo kryo, Input input, Class<NonTransactionalCommit> type) {
+ NonTransactionalCommit commit = new NonTransactionalCommit();
+ commit.valueSet.addAll((Collection<byte[]>) kryo.readClassAndObject(input));
+ return commit;
+ }
+ }, NonTransactionalCommit.class)
+ .build());
+
+ private AtomicLong globalVersion = new AtomicLong(1);
+ private Map<Long, RaftSession> listeners = new LinkedHashMap<>();
+ private Map<String, MapEntryValue> backingMap = Maps.newHashMap();
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeLong(globalVersion.get());
+ writer.writeObject(Sets.newHashSet(listeners.keySet()), serializer::encode);
+ writer.writeObject(backingMap, serializer::encode);
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ globalVersion = new AtomicLong(reader.readLong());
+
+ listeners = new LinkedHashMap<>();
+ for (Long sessionId : reader.<Set<Long>>readObject(serializer::decode)) {
+ listeners.put(sessionId, getSessions().getSession(sessionId));
+ }
+
+ backingMap = reader.readObject(serializer::decode);
+ }
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
+ executor.register(SIZE, this::size, serializer::encode);
+ executor.register(IS_EMPTY, this::isEmpty, serializer::encode);
+ executor.register(CONTAINS_KEY, serializer::decode, this::containsKey, serializer::encode);
+ executor.register(CONTAINS_VALUE, serializer::decode, this::containsValue, serializer::encode);
+ executor.register(CONTAINS_ENTRY, serializer::decode, this::containsEntry, serializer::encode);
+ executor.register(CLEAR, this::clear);
+ executor.register(KEY_SET, this::keySet, serializer::encode);
+ executor.register(KEYS, this::keys, serializer::encode);
+ executor.register(VALUES, this::values, serializer::encode);
+ executor.register(ENTRIES, this::entries, serializer::encode);
+ executor.register(GET, serializer::decode, this::get, serializer::encode);
+ executor.register(REMOVE_ALL, serializer::decode, this::removeAll, serializer::encode);
+ executor.register(REMOVE, serializer::decode, this::multiRemove, serializer::encode);
+ executor.register(PUT, serializer::decode, this::put, serializer::encode);
+ executor.register(REPLACE, serializer::decode, this::replace, serializer::encode);
+ executor.register(ADD_LISTENER, this::listen);
+ executor.register(REMOVE_LISTENER, this::unlisten);
+ }
+
+ @Override
+ public void onExpire(RaftSession session) {
+ listeners.remove(session.sessionId().id());
+ }
+
+ @Override
+ public void onClose(RaftSession session) {
+ listeners.remove(session.sessionId().id());
+ }
+
+ /**
+ * Handles a Size commit.
+ *
+ * @param commit Size commit
+ * @return number of unique key value pairs in the multimap
+ */
+ protected int size(Commit<Void> commit) {
+ return backingMap.values()
+ .stream()
+ .map(valueCollection -> valueCollection.values().size())
+ .collect(Collectors.summingInt(size -> size));
+ }
+
+ /**
+ * Handles an IsEmpty commit.
+ *
+ * @param commit IsEmpty commit
+ * @return true if the multimap contains no key-value pairs, else false
+ */
+ protected boolean isEmpty(Commit<Void> commit) {
+ return backingMap.isEmpty();
+ }
+
+ /**
+ * Handles a contains key commit.
+ *
+ * @param commit ContainsKey commit
+ * @return returns true if the key is in the multimap, else false
+ */
+ protected boolean containsKey(Commit<? extends ContainsKey> commit) {
+ return backingMap.containsKey(commit.value().key());
+ }
+
+ /**
+ * Handles a ContainsValue commit.
+ *
+ * @param commit ContainsValue commit
+ * @return true if the value is in the multimap, else false
+ */
+ protected boolean containsValue(Commit<? extends ContainsValue> commit) {
+ if (backingMap.values().isEmpty()) {
+ return false;
+ }
+ Match<byte[]> match = Match.ifValue(commit.value().value());
+ return backingMap
+ .values()
+ .stream()
+ .anyMatch(valueList ->
+ valueList
+ .values()
+ .stream()
+ .anyMatch(byteValue ->
+ match.matches(byteValue)));
+ }
+
+ /**
+ * Handles a ContainsEntry commit.
+ *
+ * @param commit ContainsEntry commit
+ * @return true if the key-value pair exists, else false
+ */
+ protected boolean containsEntry(Commit<? extends ContainsEntry> commit) {
+ MapEntryValue entryValue =
+ backingMap.get(commit.value().key());
+ if (entryValue == null) {
+ return false;
+ } else {
+ Match valueMatch = Match.ifValue(commit.value().value());
+ return entryValue
+ .values()
+ .stream()
+ .anyMatch(byteValue -> valueMatch.matches(byteValue));
+ }
+ }
+
+ /**
+ * Handles a Clear commit.
+ *
+ * @param commit Clear commit
+ */
+ protected void clear(Commit<Void> commit) {
+ backingMap.clear();
+ }
+
+ /**
+ * Handles a KeySet commit.
+ *
+ * @param commit KeySet commit
+ * @return a set of all keys in the multimap
+ */
+ protected Set<String> keySet(Commit<Void> commit) {
+ return ImmutableSet.copyOf(backingMap.keySet());
+ }
+
+ /**
+ * Handles a Keys commit.
+ *
+ * @param commit Keys commit
+ * @return a multiset of keys with each key included an equal number of
+ * times to the total key-value pairs in which that key participates
+ */
+ protected Multiset<String> keys(Commit<Void> commit) {
+ Multiset keys = HashMultiset.create();
+ backingMap.forEach((key, mapEntryValue) -> {
+ keys.add(key, mapEntryValue.values().size());
+ });
+ return keys;
+ }
+
+ /**
+ * Handles a Values commit.
+ *
+ * @param commit Values commit
+ * @return the set of values in the multimap with duplicates included
+ */
+ protected Multiset<byte[]> values(Commit<Void> commit) {
+ return backingMap
+ .values()
+ .stream()
+ .collect(new HashMultisetValueCollector());
+ }
+
+ /**
+ * Handles an Entries commit.
+ *
+ * @param commit Entries commit
+ * @return a set of all key-value pairs in the multimap
+ */
+ protected Collection<Map.Entry<String, byte[]>> entries(Commit<Void> commit) {
+ return backingMap
+ .entrySet()
+ .stream()
+ .collect(new EntrySetCollector());
+ }
+
+ /**
+ * Handles a Get commit.
+ *
+ * @param commit Get commit
+ * @return the collection of values associated with the key or an empty
+ * list if none exist
+ */
+ protected Versioned<Collection<? extends byte[]>> get(Commit<? extends Get> commit) {
+ return toVersioned(backingMap.get(commit.value().key()));
+ }
+
+ /**
+ * Handles a removeAll commit, and returns the previous mapping.
+ *
+ * @param commit removeAll commit
+ * @return collection of removed values
+ */
+ protected Versioned<Collection<? extends byte[]>> removeAll(Commit<? extends RemoveAll> commit) {
+ String key = commit.value().key();
+
+ if (!backingMap.containsKey(key)) {
+ return new Versioned<>(Sets.newHashSet(), -1);
+ }
+
+ Versioned<Collection<? extends byte[]>> removedValues =
+ backingMap.get(key).addCommit(commit);
+ publish(removedValues.value().stream()
+ .map(value -> new MultimapEvent<String, byte[]>(
+ "", key, null, value))
+ .collect(Collectors.toList()));
+ return removedValues;
+ }
+
+ /**
+ * Handles a multiRemove commit, returns true if the remove results in any
+ * change.
+ * @param commit multiRemove commit
+ * @return true if any change results, else false
+ */
+ protected boolean multiRemove(Commit<? extends MultiRemove> commit) {
+ String key = commit.value().key();
+
+ if (!backingMap.containsKey(key)) {
+ return false;
+ }
+
+ Versioned<Collection<? extends byte[]>> removedValues = backingMap
+ .get(key)
+ .addCommit(commit);
+
+ if (removedValues != null) {
+ if (removedValues.value().isEmpty()) {
+ backingMap.remove(key);
+ }
+
+ publish(removedValues.value().stream()
+ .map(value -> new MultimapEvent<String, byte[]>(
+ "", key, null, value))
+ .collect(Collectors.toList()));
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handles a put commit, returns true if any change results from this
+ * commit.
+ * @param commit a put commit
+ * @return true if this commit results in a change, else false
+ */
+ protected boolean put(Commit<? extends Put> commit) {
+ String key = commit.value().key();
+ if (commit.value().values().isEmpty()) {
+ return false;
+ }
+ if (!backingMap.containsKey(key)) {
+ backingMap.put(key, new NonTransactionalCommit());
+ }
+
+ Versioned<Collection<? extends byte[]>> addedValues = backingMap
+ .get(key)
+ .addCommit(commit);
+
+ if (addedValues != null) {
+ publish(addedValues.value().stream()
+ .map(value -> new MultimapEvent<String, byte[]>(
+ "", key, value, null))
+ .collect(Collectors.toList()));
+ return true;
+ }
+
+ return false;
+ }
+
+ protected Versioned<Collection<? extends byte[]>> replace(
+ Commit<? extends Replace> commit) {
+ if (!backingMap.containsKey(commit.value().key())) {
+ backingMap.put(commit.value().key(),
+ new NonTransactionalCommit());
+ }
+ return backingMap.get(commit.value().key()).addCommit(commit);
+ }
+
+ /**
+ * Handles a listen commit.
+ *
+ * @param commit listen commit
+ */
+ protected void listen(Commit<Void> commit) {
+ listeners.put(commit.session().sessionId().id(), commit.session());
+ }
+
+ /**
+ * Handles an unlisten commit.
+ *
+ * @param commit unlisten commit
+ */
+ protected void unlisten(Commit<Void> commit) {
+ listeners.remove(commit.session().sessionId().id());
+ }
+
+ /**
+ * Publishes events to listeners.
+ *
+ * @param events list of map event to publish
+ */
+ private void publish(List<MultimapEvent<String, byte[]>> events) {
+ listeners.values().forEach(session -> session.publish(CHANGE, serializer::encode, events));
+ }
+
+ private interface MapEntryValue {
+
+ /**
+ * Returns the list of raw {@code byte[]'s}.
+ *
+ * @return list of raw values
+ */
+ Collection<? extends byte[]> values();
+
+ /**
+ * Returns the version of the value.
+ *
+ * @return version
+ */
+ long version();
+
+ /**
+ * Add a new commit and modifies the set of values accordingly.
+ * In the case of a replace or removeAll it returns the set of removed
+ * values. In the case of put or multiRemove it returns null for no
+ * change and a set of the added or removed values respectively if a
+ * change resulted.
+ *
+ * @param commit the commit to be added
+ */
+ Versioned<Collection<? extends byte[]>> addCommit(
+ Commit<? extends MultimapOperation> commit);
+ }
+
+ private class NonTransactionalCommit implements MapEntryValue {
+ private long version;
+ private final TreeSet<byte[]> valueSet = Sets.newTreeSet(new ByteArrayComparator());
+
+ public NonTransactionalCommit() {
+ //Set the version to current it will only be updated once this is
+ // populated
+ this.version = globalVersion.get();
+ }
+
+ @Override
+ public Collection<? extends byte[]> values() {
+ return ImmutableSet.copyOf(valueSet);
+ }
+
+ @Override
+ public long version() {
+ return version;
+ }
+
+ @Override
+ public Versioned<Collection<? extends byte[]>> addCommit(
+ Commit<? extends MultimapOperation> commit) {
+ Preconditions.checkNotNull(commit);
+ Preconditions.checkNotNull(commit.value());
+ Versioned<Collection<? extends byte[]>> retVersion;
+
+ if (commit.value() instanceof Put) {
+ //Using a treeset here sanitizes the input, removing duplicates
+ Set<byte[]> valuesToAdd =
+ Sets.newTreeSet(new ByteArrayComparator());
+ ((Put) commit.value()).values().forEach(value -> {
+ if (!valueSet.contains(value)) {
+ valuesToAdd.add(value);
+ }
+ });
+ if (valuesToAdd.isEmpty()) {
+ //Do not increment or add the commit if no change resulted
+ return null;
+ }
+ retVersion = new Versioned<>(valuesToAdd, version);
+ valuesToAdd.forEach(value -> valueSet.add(value));
+ version++;
+ return retVersion;
+
+ } else if (commit.value() instanceof Replace) {
+ //Will this work?? Need to check before check-in!
+ Set<byte[]> removedValues = Sets.newHashSet();
+ removedValues.addAll(valueSet);
+ retVersion = new Versioned<>(removedValues, version);
+ valueSet.clear();
+ Set<byte[]> valuesToAdd =
+ Sets.newTreeSet(new ByteArrayComparator());
+ ((Replace) commit.value()).values().forEach(value -> {
+ valuesToAdd.add(value);
+ });
+ if (valuesToAdd.isEmpty()) {
+ version = globalVersion.incrementAndGet();
+ backingMap.remove(((Replace) commit.value()).key());
+ return retVersion;
+ }
+ valuesToAdd.forEach(value -> valueSet.add(value));
+ version = globalVersion.incrementAndGet();
+ return retVersion;
+
+ } else if (commit.value() instanceof RemoveAll) {
+ Set<byte[]> removed = Sets.newHashSet();
+ //We can assume here that values only appear once and so we
+ //do not need to sanitize the return for duplicates.
+ removed.addAll(valueSet);
+ retVersion = new Versioned<>(removed, version);
+ valueSet.clear();
+ //In the case of a removeAll all commits will be removed and
+ //unlike the multiRemove case we do not need to consider
+ //dependencies among additive and removal commits.
+
+ //Save the key for use after the commit is closed
+ String key = ((RemoveAll) commit.value()).key();
+ version = globalVersion.incrementAndGet();
+ backingMap.remove(key);
+ return retVersion;
+
+ } else if (commit.value() instanceof MultiRemove) {
+ //Must first calculate how many commits the removal depends on.
+ //At this time we also sanitize the removal set by adding to a
+ //set with proper handling of byte[] equality.
+ Set<byte[]> removed = Sets.newHashSet();
+ ((MultiRemove) commit.value()).values().forEach(value -> {
+ if (valueSet.contains(value)) {
+ removed.add(value);
+ }
+ });
+ //If there is nothing to be removed no action should be taken.
+ if (removed.isEmpty()) {
+ return null;
+ }
+ //Save key in case countdown results in closing the commit.
+ String removedKey = ((MultiRemove) commit.value()).key();
+ removed.forEach(removedValue -> {
+ valueSet.remove(removedValue);
+ });
+ //The version is updated locally as well as globally even if
+ //this object will be removed from the map in case any other
+ //party still holds a reference to this object.
+ retVersion = new Versioned<>(removed, version);
+ version = globalVersion.incrementAndGet();
+ if (valueSet.isEmpty()) {
+ backingMap.remove(removedKey);
+ }
+ return retVersion;
+
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ /**
+ * A collector that creates MapEntryValues and creates a multiset of all
+ * values in the map an equal number of times to the number of sets in
+ * which they participate.
+ */
+ private class HashMultisetValueCollector implements
+ Collector<MapEntryValue,
+ HashMultiset<byte[]>,
+ HashMultiset<byte[]>> {
+
+ @Override
+ public Supplier<HashMultiset<byte[]>> supplier() {
+ return HashMultiset::create;
+ }
+
+ @Override
+ public BiConsumer<HashMultiset<byte[]>, MapEntryValue> accumulator() {
+ return (multiset, mapEntryValue) ->
+ multiset.addAll(mapEntryValue.values());
+ }
+
+ @Override
+ public BinaryOperator<HashMultiset<byte[]>> combiner() {
+ return (setOne, setTwo) -> {
+ setOne.addAll(setTwo);
+ return setOne;
+ };
+ }
+
+ @Override
+ public Function<HashMultiset<byte[]>,
+ HashMultiset<byte[]>> finisher() {
+ return Function.identity();
+ }
+
+ @Override
+ public Set<Characteristics> characteristics() {
+ return EnumSet.of(Characteristics.UNORDERED);
+ }
+ }
+
+ /**
+ * A collector that creates Entries of {@code <String, MapEntryValue>} and
+ * creates a set of entries all key value pairs in the map.
+ */
+ private class EntrySetCollector implements
+ Collector<Map.Entry<String, MapEntryValue>,
+ Set<Map.Entry<String, byte[]>>,
+ Set<Map.Entry<String, byte[]>>> {
+ private Set<Map.Entry<String, byte[]>> set = null;
+
+ @Override
+ public Supplier<Set<Map.Entry<String, byte[]>>> supplier() {
+ return () -> {
+ if (set == null) {
+ set = Sets.newHashSet();
+ }
+ return set;
+ };
+ }
+
+ @Override
+ public BiConsumer<Set<Map.Entry<String, byte[]>>,
+ Map.Entry<String, MapEntryValue>> accumulator() {
+ return (set, entry) -> {
+ entry
+ .getValue()
+ .values()
+ .forEach(byteValue ->
+ set.add(Maps.immutableEntry(entry.getKey(),
+ byteValue)));
+ };
+ }
+
+ @Override
+ public BinaryOperator<Set<Map.Entry<String, byte[]>>> combiner() {
+ return (setOne, setTwo) -> {
+ setOne.addAll(setTwo);
+ return setOne;
+ };
+ }
+
+ @Override
+ public Function<Set<Map.Entry<String, byte[]>>,
+ Set<Map.Entry<String, byte[]>>> finisher() {
+ return (unused) -> set;
+ }
+
+ @Override
+ public Set<Characteristics> characteristics() {
+ return EnumSet.of(Characteristics.UNORDERED);
+ }
+ }
+ /**
+ * Utility for turning a {@code MapEntryValue} to {@code Versioned}.
+ * @param value map entry value
+ * @return versioned instance or an empty list versioned -1 if argument is
+ * null
+ */
+ private Versioned<Collection<? extends byte[]>> toVersioned(
+ MapEntryValue value) {
+ return value == null ? new Versioned<>(Lists.newArrayList(), -1) :
+ new Versioned<>(value.values(),
+ value.version());
+ }
+
+ private static class ByteArrayComparator implements Comparator<byte[]> {
+
+ @Override
+ public int compare(byte[] o1, byte[] o2) {
+ if (Arrays.equals(o1, o2)) {
+ return 0;
+ } else {
+ for (int i = 0; i < o1.length && i < o2.length; i++) {
+ if (o1[i] < o2[i]) {
+ return -1;
+ } else if (o1[i] > o2[i]) {
+ return 1;
+ }
+ }
+ return o1.length > o2.length ? 1 : -1;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapState.java
deleted file mode 100644
index 47972d1..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapState.java
+++ /dev/null
@@ -1,805 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multiset;
-import com.google.common.collect.Sets;
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.Snapshottable;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.session.ServerSession;
-import io.atomix.copycat.server.session.SessionListener;
-import io.atomix.copycat.server.storage.snapshot.SnapshotReader;
-import io.atomix.copycat.server.storage.snapshot.SnapshotWriter;
-import io.atomix.resource.ResourceStateMachine;
-import org.onlab.util.CountDownCompleter;
-import org.onlab.util.Match;
-import org.onosproject.store.service.MultimapEvent;
-import org.onosproject.store.service.Versioned;
-import org.slf4j.Logger;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.BiConsumer;
-import java.util.function.BinaryOperator;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Clear;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsValue;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Entries;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Get;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.IsEmpty;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.KeySet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Keys;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Listen;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.MultiRemove;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.MultimapCommand;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Put;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.RemoveAll;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Replace;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Size;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Unlisten;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Values;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * State Machine for {@link AtomixConsistentSetMultimap} resource.
- */
-public class AtomixConsistentSetMultimapState extends ResourceStateMachine
- implements SessionListener, Snapshottable {
-
- private final Logger log = getLogger(getClass());
- private final AtomicLong globalVersion = new AtomicLong(1);
- private final Map<Long, Commit<? extends Listen>> listeners = new HashMap<>();
- private final Map<String, MapEntryValue> backingMap = Maps.newHashMap();
-
- public AtomixConsistentSetMultimapState(Properties properties) {
- super(properties);
- }
-
- @Override
- public void snapshot(SnapshotWriter writer) {
- }
-
- @Override
- public void install(SnapshotReader reader) {
- }
-
- @Override
- protected void configure(StateMachineExecutor executor) {
- executor.register(Size.class, this::size);
- executor.register(IsEmpty.class, this::isEmpty);
- executor.register(ContainsKey.class, this::containsKey);
- executor.register(ContainsValue.class, this::containsValue);
- executor.register(ContainsEntry.class, this::containsEntry);
- executor.register(Clear.class, this::clear);
- executor.register(KeySet.class, this::keySet);
- executor.register(Keys.class, this::keys);
- executor.register(Values.class, this::values);
- executor.register(Entries.class, this::entries);
- executor.register(Get.class, this::get);
- executor.register(RemoveAll.class, this::removeAll);
- executor.register(MultiRemove.class, this::multiRemove);
- executor.register(Put.class, this::put);
- executor.register(Replace.class, this::replace);
- executor.register(Listen.class, this::listen);
- executor.register(Unlisten.class, this::unlisten);
- }
-
- /**
- * Handles a Size commit.
- *
- * @param commit Size commit
- * @return number of unique key value pairs in the multimap
- */
- protected int size(Commit<? extends Size> commit) {
- try {
- return backingMap.values()
- .stream()
- .map(valueCollection -> valueCollection.values().size())
- .collect(Collectors.summingInt(size -> size));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles an IsEmpty commit.
- *
- * @param commit IsEmpty commit
- * @return true if the multimap contains no key-value pairs, else false
- */
- protected boolean isEmpty(Commit<? extends IsEmpty> commit) {
- try {
- return backingMap.isEmpty();
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a contains key commit.
- *
- * @param commit ContainsKey commit
- * @return returns true if the key is in the multimap, else false
- */
- protected boolean containsKey(Commit<? extends ContainsKey> commit) {
- try {
- return backingMap.containsKey(commit.operation().key());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a ContainsValue commit.
- *
- * @param commit ContainsValue commit
- * @return true if the value is in the multimap, else false
- */
- protected boolean containsValue(Commit<? extends ContainsValue> commit) {
- try {
- if (backingMap.values().isEmpty()) {
- return false;
- }
- Match<byte[]> match = Match.ifValue(commit.operation().value());
- return backingMap
- .values()
- .stream()
- .anyMatch(valueList ->
- valueList
- .values()
- .stream()
- .anyMatch(byteValue ->
- match.matches(byteValue)));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a ContainsEntry commit.
- *
- * @param commit ContainsEntry commit
- * @return true if the key-value pair exists, else false
- */
- protected boolean containsEntry(Commit<? extends ContainsEntry> commit) {
- try {
- MapEntryValue entryValue =
- backingMap.get(commit.operation().key());
- if (entryValue == null) {
- return false;
- } else {
- Match valueMatch = Match.ifValue(commit.operation().value());
- return entryValue
- .values()
- .stream()
- .anyMatch(byteValue -> valueMatch.matches(byteValue));
- }
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a Clear commit.
- *
- * @param commit Clear commit
- */
- protected void clear(Commit<? extends Clear> commit) {
- try {
- backingMap.clear();
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a KeySet commit.
- *
- * @param commit KeySet commit
- * @return a set of all keys in the multimap
- */
- protected Set<String> keySet(Commit<? extends KeySet> commit) {
- try {
- return ImmutableSet.copyOf(backingMap.keySet());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a Keys commit.
- *
- * @param commit Keys commit
- * @return a multiset of keys with each key included an equal number of
- * times to the total key-value pairs in which that key participates
- */
- protected Multiset<String> keys(Commit<? extends Keys> commit) {
- try {
- Multiset keys = HashMultiset.create();
- backingMap.forEach((key, mapEntryValue) -> {
- keys.add(key, mapEntryValue.values().size());
- });
- return keys;
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a Values commit.
- *
- * @param commit Values commit
- * @return the set of values in the multimap with duplicates included
- */
- protected Multiset<byte[]> values(Commit<? extends Values> commit) {
- try {
- return backingMap
- .values()
- .stream()
- .collect(new HashMultisetValueCollector());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles an Entries commit.
- *
- * @param commit Entries commit
- * @return a set of all key-value pairs in the multimap
- */
- protected Collection<Map.Entry<String, byte[]>> entries(
- Commit<? extends Entries> commit) {
- try {
- return backingMap
- .entrySet()
- .stream()
- .collect(new EntrySetCollector());
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a Get commit.
- *
- * @param commit Get commit
- * @return the collection of values associated with the key or an empty
- * list if none exist
- */
- protected Versioned<Collection<? extends byte[]>> get(
- Commit<? extends Get> commit) {
- try {
- MapEntryValue mapEntryValue = backingMap.get(commit.operation().key());
- return toVersioned(backingMap.get(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- /**
- * Handles a removeAll commit, and returns the previous mapping.
- *
- * @param commit removeAll commit
- * @return collection of removed values
- */
- protected Versioned<Collection<? extends byte[]>> removeAll(
- Commit<? extends RemoveAll> commit) {
- String key = commit.operation().key();
-
- if (!backingMap.containsKey(key)) {
- commit.close();
- return new Versioned<>(Sets.newHashSet(), -1);
- }
-
- Versioned<Collection<? extends byte[]>> removedValues =
- backingMap.get(key).addCommit(commit);
- publish(removedValues.value().stream()
- .map(value -> new MultimapEvent<String, byte[]>(
- "", key, null, value))
- .collect(Collectors.toList()));
- return removedValues;
- }
-
- /**
- * Handles a multiRemove commit, returns true if the remove results in any
- * change.
- * @param commit multiRemove commit
- * @return true if any change results, else false
- */
- protected boolean multiRemove(Commit<? extends MultiRemove> commit) {
- String key = commit.operation().key();
-
- if (!backingMap.containsKey(key)) {
- commit.close();
- return false;
- }
-
- Versioned<Collection<? extends byte[]>> removedValues = backingMap
- .get(key)
- .addCommit(commit);
-
- if (removedValues != null) {
- publish(removedValues.value().stream()
- .map(value -> new MultimapEvent<String, byte[]>(
- "", key, null, value))
- .collect(Collectors.toList()));
- return true;
- }
-
- return false;
- }
-
- /**
- * Handles a put commit, returns true if any change results from this
- * commit.
- * @param commit a put commit
- * @return true if this commit results in a change, else false
- */
- protected boolean put(Commit<? extends Put> commit) {
- String key = commit.operation().key();
- if (commit.operation().values().isEmpty()) {
- return false;
- }
- if (!backingMap.containsKey(key)) {
- backingMap.put(key, new NonTransactionalCommit(1));
- }
-
- Versioned<Collection<? extends byte[]>> addedValues = backingMap
- .get(key)
- .addCommit(commit);
-
- if (addedValues != null) {
- publish(addedValues.value().stream()
- .map(value -> new MultimapEvent<String, byte[]>(
- "", key, value, null))
- .collect(Collectors.toList()));
- return true;
- }
-
- return false;
- }
-
- protected Versioned<Collection<? extends byte[]>> replace(
- Commit<? extends Replace> commit) {
- if (!backingMap.containsKey(commit.operation().key())) {
- backingMap.put(commit.operation().key(),
- new NonTransactionalCommit(1));
- }
- return backingMap.get(commit.operation().key()).addCommit(commit);
- }
-
- /**
- * Handles a listen commit.
- *
- * @param commit listen commit
- */
- protected void listen(Commit<? extends Listen> commit) {
- Long sessionId = commit.session().id();
- if (listeners.putIfAbsent(sessionId, commit) != null) {
- commit.close();
- return;
- }
- commit.session()
- .onStateChange(
- state -> {
- if (state == ServerSession.State.CLOSED
- || state == ServerSession.State.EXPIRED) {
- Commit<? extends Listen> listener = listeners.remove(sessionId);
- if (listener != null) {
- listener.close();
- }
- }
- });
- }
-
- /**
- * Handles an unlisten commit.
- *
- * @param commit unlisten commit
- */
- protected void unlisten(Commit<? extends Unlisten> commit) {
- try {
- Commit<? extends Listen> listener = listeners.remove(commit.session().id());
- if (listener != null) {
- listener.close();
- }
- } finally {
- commit.close();
- }
- }
-
- /**
- * Publishes events to listeners.
- *
- * @param events list of map event to publish
- */
- private void publish(List<MultimapEvent<String, byte[]>> events) {
- listeners.values().forEach(commit ->
- commit.session().publish(AtomixConsistentSetMultimap.CHANGE_SUBJECT, events));
- }
-
- private interface MapEntryValue {
-
- /**
- * Returns the list of raw {@code byte[]'s}.
- *
- * @return list of raw values
- */
- Collection<? extends byte[]> values();
-
- /**
- * Returns the version of the value.
- *
- * @return version
- */
- long version();
-
- /**
- * Discards the value by invoke appropriate clean up actions.
- */
- void discard();
-
- /**
- * Add a new commit and modifies the set of values accordingly.
- * In the case of a replace or removeAll it returns the set of removed
- * values. In the case of put or multiRemove it returns null for no
- * change and a set of the added or removed values respectively if a
- * change resulted.
- *
- * @param commit the commit to be added
- */
- Versioned<Collection<? extends byte[]>> addCommit(
- Commit<? extends MultimapCommand> commit);
- }
-
- private class NonTransactionalCommit implements MapEntryValue {
- private long version;
- private final TreeMap<byte[], CountDownCompleter<Commit>>
- valueCountdownMap = Maps.newTreeMap(new ByteArrayComparator());
- /*This is a mapping of commits that added values to the commits
- * removing those values, they will not be circular because keys will
- * be exclusively Put and Replace commits and values will be exclusively
- * Multiremove commits, each time a Put or replace is removed it should
- * as part of closing go through and countdown each of the remove
- * commits depending on it.*/
- private final HashMultimap<Commit, CountDownCompleter<Commit>>
- additiveToRemovalCommits = HashMultimap.create();
-
- public NonTransactionalCommit(
- long version) {
- //Set the version to current it will only be updated once this is
- // populated
- this.version = globalVersion.get();
- }
-
- @Override
- public Collection<? extends byte[]> values() {
- return ImmutableSet.copyOf(valueCountdownMap.keySet());
- }
-
- @Override
- public long version() {
- return version;
- }
-
- @Override
- public void discard() {
- valueCountdownMap.values().forEach(completer ->
- completer.object().close());
- }
-
- @Override
- public Versioned<Collection<? extends byte[]>> addCommit(
- Commit<? extends MultimapCommand> commit) {
- Preconditions.checkNotNull(commit);
- Preconditions.checkNotNull(commit.operation());
- Versioned<Collection<? extends byte[]>> retVersion;
-
- if (commit.operation() instanceof Put) {
- //Using a treeset here sanitizes the input, removing duplicates
- Set<byte[]> valuesToAdd =
- Sets.newTreeSet(new ByteArrayComparator());
- ((Put) commit.operation()).values().forEach(value -> {
- if (!valueCountdownMap.containsKey(value)) {
- valuesToAdd.add(value);
- }
- });
- if (valuesToAdd.isEmpty()) {
- //Do not increment or add the commit if no change resulted
- commit.close();
- return null;
- }
- //When all values from a commit have been removed decrement all
- //removal commits relying on it and remove itself from the
- //mapping of additive commits to the commits removing the
- //values it added. (Only multiremoves will be dependent)
- CountDownCompleter<Commit> completer =
- new CountDownCompleter<>(commit, valuesToAdd.size(),
- c -> {
- if (additiveToRemovalCommits.containsKey(c)) {
- additiveToRemovalCommits.
- get(c).
- forEach(countdown ->
- countdown.countDown());
- additiveToRemovalCommits.removeAll(c);
- }
- c.close();
- });
- retVersion = new Versioned<>(valuesToAdd, version);
- valuesToAdd.forEach(value -> valueCountdownMap.put(value,
- completer));
- version++;
- return retVersion;
-
- } else if (commit.operation() instanceof Replace) {
- //Will this work?? Need to check before check-in!
- Set<byte[]> removedValues = Sets.newHashSet();
- removedValues.addAll(valueCountdownMap.keySet());
- retVersion = new Versioned<>(removedValues, version);
- valueCountdownMap.values().forEach(countdown ->
- countdown.countDown());
- valueCountdownMap.clear();
- Set<byte[]> valuesToAdd =
- Sets.newTreeSet(new ByteArrayComparator());
- ((Replace) commit.operation()).values().forEach(value -> {
- valuesToAdd.add(value);
- });
- if (valuesToAdd.isEmpty()) {
- version = globalVersion.incrementAndGet();
- backingMap.remove(((Replace) commit.operation()).key());
- //Order is important here, the commit must be closed last
- //(or minimally after all uses)
- commit.close();
- return retVersion;
- }
- CountDownCompleter<Commit> completer =
- new CountDownCompleter<>(commit, valuesToAdd.size(),
- c -> {
- if (additiveToRemovalCommits
- .containsKey(c)) {
- additiveToRemovalCommits.
- get(c).
- forEach(countdown ->
- countdown.countDown());
- additiveToRemovalCommits.
- removeAll(c);
- }
- c.close();
- });
- valuesToAdd.forEach(value ->
- valueCountdownMap.put(value, completer));
- version = globalVersion.incrementAndGet();
- return retVersion;
-
- } else if (commit.operation() instanceof RemoveAll) {
- Set<byte[]> removed = Sets.newHashSet();
- //We can assume here that values only appear once and so we
- //do not need to sanitize the return for duplicates.
- removed.addAll(valueCountdownMap.keySet());
- retVersion = new Versioned<>(removed, version);
- valueCountdownMap.values().forEach(countdown ->
- countdown.countDown());
- valueCountdownMap.clear();
- //In the case of a removeAll all commits will be removed and
- //unlike the multiRemove case we do not need to consider
- //dependencies among additive and removal commits.
-
- //Save the key for use after the commit is closed
- String key = ((RemoveAll) commit.operation()).key();
- commit.close();
- version = globalVersion.incrementAndGet();
- backingMap.remove(key);
- return retVersion;
-
- } else if (commit.operation() instanceof MultiRemove) {
- //Must first calculate how many commits the removal depends on.
- //At this time we also sanitize the removal set by adding to a
- //set with proper handling of byte[] equality.
- Set<byte[]> removed = Sets.newHashSet();
- Set<Commit> commitsRemovedFrom = Sets.newHashSet();
- ((MultiRemove) commit.operation()).values().forEach(value -> {
- if (valueCountdownMap.containsKey(value)) {
- removed.add(value);
- commitsRemovedFrom
- .add(valueCountdownMap.get(value).object());
- }
- });
- //If there is nothing to be removed no action should be taken.
- if (removed.isEmpty()) {
- //Do not increment or add the commit if no change resulted
- commit.close();
- return null;
- }
- //When all additive commits this depends on are closed this can
- //be closed as well.
- CountDownCompleter<Commit> completer =
- new CountDownCompleter<>(commit,
- commitsRemovedFrom.size(),
- c -> c.close());
- commitsRemovedFrom.forEach(commitRemovedFrom -> {
- additiveToRemovalCommits.put(commitRemovedFrom, completer);
- });
- //Save key in case countdown results in closing the commit.
- String removedKey = ((MultiRemove) commit.operation()).key();
- removed.forEach(removedValue -> {
- valueCountdownMap.remove(removedValue).countDown();
- });
- //The version is updated locally as well as globally even if
- //this object will be removed from the map in case any other
- //party still holds a reference to this object.
- retVersion = new Versioned<>(removed, version);
- version = globalVersion.incrementAndGet();
- if (valueCountdownMap.isEmpty()) {
- backingMap
- .remove(removedKey);
- }
- return retVersion;
-
- } else {
- throw new IllegalArgumentException();
- }
- }
- }
-
- /**
- * A collector that creates MapEntryValues and creates a multiset of all
- * values in the map an equal number of times to the number of sets in
- * which they participate.
- */
- private class HashMultisetValueCollector implements
- Collector<MapEntryValue,
- HashMultiset<byte[]>,
- HashMultiset<byte[]>> {
-
- @Override
- public Supplier<HashMultiset<byte[]>> supplier() {
- return HashMultiset::create;
- }
-
- @Override
- public BiConsumer<HashMultiset<byte[]>, MapEntryValue> accumulator() {
- return (multiset, mapEntryValue) ->
- multiset.addAll(mapEntryValue.values());
- }
-
- @Override
- public BinaryOperator<HashMultiset<byte[]>> combiner() {
- return (setOne, setTwo) -> {
- setOne.addAll(setTwo);
- return setOne;
- };
- }
-
- @Override
- public Function<HashMultiset<byte[]>,
- HashMultiset<byte[]>> finisher() {
- return Function.identity();
- }
-
- @Override
- public Set<Characteristics> characteristics() {
- return EnumSet.of(Characteristics.UNORDERED);
- }
- }
-
- /**
- * A collector that creates Entries of {@code <String, MapEntryValue>} and
- * creates a set of entries all key value pairs in the map.
- */
- private class EntrySetCollector implements
- Collector<Map.Entry<String, MapEntryValue>,
- Set<Map.Entry<String, byte[]>>,
- Set<Map.Entry<String, byte[]>>> {
- private Set<Map.Entry<String, byte[]>> set = null;
-
- @Override
- public Supplier<Set<Map.Entry<String, byte[]>>> supplier() {
- return () -> {
- if (set == null) {
- set = Sets.newHashSet();
- }
- return set;
- };
- }
-
- @Override
- public BiConsumer<Set<Map.Entry<String, byte[]>>,
- Map.Entry<String, MapEntryValue>> accumulator() {
- return (set, entry) -> {
- entry
- .getValue()
- .values()
- .forEach(byteValue ->
- set.add(Maps.immutableEntry(entry.getKey(),
- byteValue)));
- };
- }
-
- @Override
- public BinaryOperator<Set<Map.Entry<String, byte[]>>> combiner() {
- return (setOne, setTwo) -> {
- setOne.addAll(setTwo);
- return setOne;
- };
- }
-
- @Override
- public Function<Set<Map.Entry<String, byte[]>>,
- Set<Map.Entry<String, byte[]>>> finisher() {
- return (unused) -> set;
- }
-
- @Override
- public Set<Characteristics> characteristics() {
- return EnumSet.of(Characteristics.UNORDERED);
- }
- }
- /**
- * Utility for turning a {@code MapEntryValue} to {@code Versioned}.
- * @param value map entry value
- * @return versioned instance or an empty list versioned -1 if argument is
- * null
- */
- private Versioned<Collection<? extends byte[]>> toVersioned(
- MapEntryValue value) {
- return value == null ? new Versioned<>(Lists.newArrayList(), -1) :
- new Versioned<>(value.values(),
- value.version());
- }
-
- private class ByteArrayComparator implements Comparator<byte[]> {
-
- @Override
- public int compare(byte[] o1, byte[] o2) {
- if (Arrays.equals(o1, o2)) {
- return 0;
- } else {
- for (int i = 0; i < o1.length && i < o2.length; i++) {
- if (o1[i] < o2[i]) {
- return -1;
- } else if (o1[i] > o2[i]) {
- return 1;
- }
- }
- return o1.length > o2.length ? 1 : -1;
- }
- }
- }
- }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java
index bf462c9..8f91b3d 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java
@@ -16,32 +16,12 @@
package org.onosproject.store.primitives.resources.impl;
-import com.google.common.collect.Maps;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-import org.onlab.util.Match;
-import org.onosproject.store.primitives.MapUpdate;
-import org.onosproject.store.primitives.TransactionId;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstKey;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorEntry;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherEntry;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastEntry;
-import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerEntry;
-import org.onosproject.store.service.AsyncConsistentTreeMap;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.MapEventListener;
-import org.onosproject.store.service.TransactionLog;
-import org.onosproject.store.service.Version;
-import org.onosproject.store.service.Versioned;
-
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
-import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@@ -49,114 +29,145 @@
import java.util.function.BiFunction;
import java.util.function.Predicate;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Clear;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsValue;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.EntrySet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Get;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.GetOrDefault;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.IsEmpty;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.KeySet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Listen;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollFirstEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollLastEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Size;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Unlisten;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.UpdateAndGet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Values;
+import com.google.common.collect.Maps;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.primitives.MapUpdate;
+import org.onosproject.store.primitives.TransactionId;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorEntry;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherEntry;
+import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerEntry;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.AsyncConsistentTreeMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.TransactionLog;
+import org.onosproject.store.service.Version;
+import org.onosproject.store.service.Versioned;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsValue;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ENTRY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET_OR_DEFAULT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.Get;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GetOrDefault;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.KEY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_FIRST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_LAST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SIZE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UPDATE_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UpdateAndGet;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.VALUES;
/**
* Implementation of {@link AsyncConsistentTreeMap}.
*/
-@ResourceTypeInfo(id = -155, factory = AtomixConsistentTreeMapFactory.class)
-public class AtomixConsistentTreeMap extends AbstractResource<AtomixConsistentTreeMap>
- implements AsyncConsistentTreeMap<byte[]> {
+public class AtomixConsistentTreeMap extends AbstractRaftPrimitive implements AsyncConsistentTreeMap<byte[]> {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixConsistentTreeMapOperations.NAMESPACE)
+ .register(AtomixConsistentTreeMapEvents.NAMESPACE)
+ .build());
private final Map<MapEventListener<String, byte[]>, Executor>
mapEventListeners = Maps.newConcurrentMap();
- public static final String CHANGE_SUBJECT = "changeEvents";
-
- public AtomixConsistentTreeMap(CopycatClient client, Properties options) {
- super(client, options);
- }
-
- @Override
- public String name() {
- return null;
- }
-
- @Override
- public CompletableFuture<AtomixConsistentTreeMap> open() {
- return super.open().thenApply(result -> {
- client.onEvent(CHANGE_SUBJECT, this::handleEvent);
- return result;
- });
+ public AtomixConsistentTreeMap(RaftProxy proxy) {
+ super(proxy);
+ proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent);
}
private void handleEvent(List<MapEvent<String, byte[]>> events) {
events.forEach(event -> mapEventListeners.
forEach((listener, executor) ->
- executor.execute(() ->
- listener.event(event))));
+ executor.execute(() ->
+ listener.event(event))));
}
@Override
public CompletableFuture<Boolean> isEmpty() {
- return client.submit(new IsEmpty());
+ return proxy.invoke(IS_EMPTY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Integer> size() {
- return client.submit(new Size());
+ return proxy.invoke(SIZE, SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsKey(String key) {
- return client.submit(new ContainsKey(key));
+ return proxy.invoke(CONTAINS_KEY, SERIALIZER::encode, new ContainsKey(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Boolean> containsValue(byte[] value) {
- return client.submit(new ContainsValue(value));
+ return proxy.invoke(CONTAINS_VALUE, SERIALIZER::encode, new ContainsValue(value), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<byte[]>> get(String key) {
- return client.submit(new Get(key));
+ return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<byte[]>> getOrDefault(String key, byte[] defaultValue) {
- return client.submit(new GetOrDefault(key, defaultValue));
+ return proxy.invoke(
+ GET_OR_DEFAULT,
+ SERIALIZER::encode,
+ new GetOrDefault(key, defaultValue),
+ SERIALIZER::decode);
}
@Override
public CompletableFuture<Set<String>> keySet() {
- return client.submit(new KeySet());
+ return proxy.invoke(KEY_SET, SERIALIZER::decode);
}
@Override
public CompletableFuture<Collection<Versioned<byte[]>>> values() {
- return client.submit(new Values());
+ return proxy.invoke(VALUES, SERIALIZER::decode);
}
@Override
public CompletableFuture<Set<Map.Entry<String, Versioned<byte[]>>>> entrySet() {
- return client.submit(new EntrySet());
+ return proxy.invoke(ENTRY_SET, SERIALIZER::decode);
}
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> put(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.ANY, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -164,7 +175,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> putAndGet(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.ANY, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.newValue());
}
@@ -172,7 +187,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> putIfAbsent(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.NULL, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.NULL, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -180,7 +199,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> remove(String key) {
- return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, null, Match.ANY, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -188,7 +211,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> remove(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@@ -196,7 +223,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> remove(String key, long version) {
- return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version)))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version)),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@@ -204,7 +235,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> replace(String key, byte[] value) {
- return client.submit(new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.oldValue());
}
@@ -212,7 +247,11 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> replace(String key, byte[] oldValue, byte[] newValue) {
- return client.submit(new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@@ -220,14 +259,18 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Boolean> replace(String key, long oldVersion, byte[] newValue) {
- return client.submit(new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion)))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion)),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.updated());
}
@Override
public CompletableFuture<Void> clear() {
- return client.submit(new Clear())
+ return proxy.<MapEntryUpdateResult.Status>invoke(CLEAR, SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r))
.thenApply(v -> null);
}
@@ -235,10 +278,10 @@
@Override
@SuppressWarnings("unchecked")
public CompletableFuture<Versioned<byte[]>> computeIf(String key,
- Predicate<? super byte[]> condition,
- BiFunction<? super String,
- ? super byte[],
- ? extends byte[]> remappingFunction) {
+ Predicate<? super byte[]> condition,
+ BiFunction<? super String,
+ ? super byte[],
+ ? extends byte[]> remappingFunction) {
return get(key).thenCompose(r1 -> {
byte[] existingValue = r1 == null ? null : r1.value();
@@ -259,8 +302,11 @@
}
Match<byte[]> valueMatch = r1 == null ? Match.NULL : Match.ANY;
Match<Long> versionMatch = r1 == null ? Match.ANY : Match.ifValue(r1.version());
- return client.submit(new UpdateAndGet(key, computedValue.get(),
- valueMatch, versionMatch))
+ return proxy.<UpdateAndGet, MapEntryUpdateResult<String, byte[]>>invoke(
+ UPDATE_AND_GET,
+ SERIALIZER::encode,
+ new UpdateAndGet(key, computedValue.get(), valueMatch, versionMatch),
+ SERIALIZER::decode)
.whenComplete((r, e) -> throwIfLocked(r.status()))
.thenApply(v -> v.newValue());
});
@@ -270,9 +316,9 @@
public CompletableFuture<Void> addListener(
MapEventListener<String, byte[]> listener, Executor executor) {
if (mapEventListeners.isEmpty()) {
- return client.submit(new Listen()).thenRun(() ->
- mapEventListeners.put(listener,
- executor));
+ return proxy.invoke(ADD_LISTENER).thenRun(() ->
+ mapEventListeners.put(listener,
+ executor));
} else {
mapEventListeners.put(listener, executor);
return CompletableFuture.completedFuture(null);
@@ -283,7 +329,7 @@
public synchronized CompletableFuture<Void> removeListener(MapEventListener<String, byte[]> listener) {
if (mapEventListeners.remove(listener) != null &&
mapEventListeners.isEmpty()) {
- return client.submit(new Unlisten())
+ return proxy.invoke(REMOVE_LISTENER)
.thenApply(v -> null);
}
return CompletableFuture.completedFuture(null);
@@ -298,74 +344,74 @@
@Override
public CompletableFuture<String> firstKey() {
- return client.submit(new FirstKey<String>());
+ return proxy.invoke(FIRST_KEY, SERIALIZER::decode);
}
@Override
public CompletableFuture<String> lastKey() {
- return client.submit(new LastKey<String>());
+ return proxy.invoke(LAST_KEY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> ceilingEntry(String key) {
- return client.submit(new CeilingEntry(key));
+ return proxy.invoke(CEILING_ENTRY, SERIALIZER::encode, new CeilingEntry(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> floorEntry(String key) {
- return client.submit(new FloorEntry<String, Versioned<byte[]>>(key));
+ return proxy.invoke(FLOOR_ENTRY, SERIALIZER::encode, new FloorEntry(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> higherEntry(
String key) {
- return client.submit(new HigherEntry<String, Versioned<byte[]>>(key));
+ return proxy.invoke(HIGHER_ENTRY, SERIALIZER::encode, new HigherEntry(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> lowerEntry(
String key) {
- return client.submit(new LowerEntry<>(key));
+ return proxy.invoke(LOWER_ENTRY, SERIALIZER::encode, new LowerEntry(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> firstEntry() {
- return client.submit(new FirstEntry());
+ return proxy.invoke(FIRST_ENTRY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> lastEntry() {
- return client.submit(new LastEntry<String, Versioned<byte[]>>());
+ return proxy.invoke(LAST_ENTRY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> pollFirstEntry() {
- return client.submit(new PollFirstEntry());
+ return proxy.invoke(POLL_FIRST_ENTRY, SERIALIZER::decode);
}
@Override
public CompletableFuture<Map.Entry<String, Versioned<byte[]>>> pollLastEntry() {
- return client.submit(new PollLastEntry());
+ return proxy.invoke(POLL_LAST_ENTRY, SERIALIZER::decode);
}
@Override
public CompletableFuture<String> lowerKey(String key) {
- return client.submit(new LowerKey(key));
+ return proxy.invoke(LOWER_KEY, SERIALIZER::encode, new LowerKey(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<String> floorKey(String key) {
- return client.submit(new FloorKey(key));
+ return proxy.invoke(FLOOR_KEY, SERIALIZER::encode, new FloorKey(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<String> ceilingKey(String key) {
- return client.submit(new CeilingKey(key));
+ return proxy.invoke(CEILING_KEY, SERIALIZER::encode, new CeilingKey(key), SERIALIZER::decode);
}
@Override
public CompletableFuture<String> higherKey(String key) {
- return client.submit(new HigherKey(key));
+ return proxy.invoke(HIGHER_KEY, SERIALIZER::encode, new HigherKey(key), SERIALIZER::decode);
}
@Override
@@ -404,4 +450,4 @@
public CompletableFuture<Void> rollback(TransactionId transactionId) {
throw new UnsupportedOperationException("This operation is not yet supported.");
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java
deleted file mode 100644
index 59cf1a5..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java
+++ /dev/null
@@ -1,700 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import com.google.common.base.MoreObjects;
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.catalyst.util.Assert;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-import org.onlab.util.Match;
-import org.onosproject.store.service.Versioned;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.NavigableSet;
-import java.util.Set;
-
-/**
- * {@link org.onosproject.store.service.AsyncConsistentTreeMap} Resource
- * state machine operations.
- */
-public final class AtomixConsistentTreeMapCommands {
-
- private AtomixConsistentTreeMapCommands() {
- }
-
- /**
- * Abstract treeMap command.
- */
- @SuppressWarnings("serial")
- public abstract static class TreeCommand<V>
- implements Command<V>, CatalystSerializable {
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- /**
- * Abstract treeMap query.
- */
- @SuppressWarnings("serial")
- public abstract static class TreeQuery<V>
- implements Query<V>, CatalystSerializable {
- @Override
- public ConsistencyLevel consistency() {
- return ConsistencyLevel.LINEARIZABLE_LEASE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> bufferOutput,
- Serializer serializer) {
-
- }
-
- @Override
- public void readObject(BufferInput<?> bufferInput,
- Serializer serializer) {
-
- }
- }
- /**
- * Abstract key-based query.
- */
- @SuppressWarnings("serial")
- public abstract static class KeyQuery<K> extends TreeQuery<K> {
- protected String key;
-
- public KeyQuery(String key) {
- this.key = Assert.notNull(key, "key");
- }
-
- public KeyQuery() {
- }
-
- public String key() {
- return key;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> bufferOutput,
- Serializer serializer) {
- super.writeObject(bufferOutput, serializer);
- serializer.writeObject(key, bufferOutput);
- }
-
- @Override
- public void readObject(BufferInput<?> bufferInput,
- Serializer serializer) {
- super.readObject(bufferInput, serializer);
- key = serializer.readObject(bufferInput);
- }
- }
-
- /**
- * Abstract value-based query.
- */
- @SuppressWarnings("serial")
- public abstract static class ValueQuery<V> extends TreeQuery<V> {
- protected byte[] value;
-
- public ValueQuery() {}
-
- public ValueQuery(byte[] value) {
- this.value = Assert.notNull(value, "value");
- }
-
- public byte[] value() {
- return value;
- }
-
- @Override
- public void writeObject(BufferOutput<?> bufferOutput,
- Serializer serializer) {
- super.writeObject(bufferOutput, serializer);
- serializer.writeObject(value, bufferOutput);
- }
-
- @Override
- public void readObject(BufferInput<?> bufferInput,
- Serializer serializer) {
- super.readObject(bufferInput, serializer);
- value = serializer.readObject(bufferInput);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("value", value)
- .toString();
- }
- }
-
- /**
- * Contains key command.
- */
- @SuppressWarnings("serial")
- public static class ContainsKey extends KeyQuery<Boolean> {
-
- public ContainsKey(String key) {
- super(key);
- }
-
- public ContainsKey() {
- }
- }
- /**
- * Contains value command.
- */
- @SuppressWarnings("serial")
- public static class ContainsValue extends ValueQuery<Boolean> {
- public ContainsValue() {
- }
-
- public ContainsValue(byte[] value) {
- super(value);
- }
-
- }
-
- /**
- * AsyncConsistentTreeMap update command.
- */
- @SuppressWarnings("serial")
- public static class UpdateAndGet
- extends TreeCommand<MapEntryUpdateResult<String, byte[]>> {
- private String key;
- private byte[] value;
- private Match<byte[]> valueMatch;
- private Match<Long> versionMatch;
- public UpdateAndGet() {
- }
-
- public UpdateAndGet(String key,
- byte[] value,
- Match<byte[]> valueMatch,
- Match<Long> versionMatch) {
- this.key = key;
- this.value = value;
- this.valueMatch = valueMatch;
- this.versionMatch = versionMatch;
- }
-
- public String key() {
- return this.key;
- }
-
- public byte[] value() {
- return this.value;
- }
-
- public Match<byte[]> valueMatch() {
- return this.valueMatch;
- }
-
- public Match<Long> versionMatch() {
- return this.versionMatch;
- }
-
- @Override
- public CompactionMode compaction() {
- return value == null ? CompactionMode.TOMBSTONE : CompactionMode.QUORUM;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(key, buffer);
- serializer.writeObject(value, buffer);
- serializer.writeObject(valueMatch, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- key = serializer.readObject(buffer);
- value = serializer.readObject(buffer);
- valueMatch = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("key", key)
- .add("value", value)
- .add("valueMatch", valueMatch)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Get query.
- */
- @SuppressWarnings("serial")
- public static class Get extends KeyQuery<Versioned<byte[]>> {
- public Get() {
- }
-
- public Get(String key) {
- super(key);
- }
- }
-
- /**
- * Get or default query.
- */
- @SuppressWarnings("serial")
- public static class GetOrDefault extends KeyQuery<Versioned<byte[]>> {
- private byte[] defaultValue;
-
- public GetOrDefault() {
- }
-
- public GetOrDefault(String key, byte[] defaultValue) {
- super(key);
- this.defaultValue = defaultValue;
- }
-
- /**
- * Returns the default value.
- *
- * @return the default value
- */
- public byte[] defaultValue() {
- return defaultValue;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(defaultValue, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- defaultValue = serializer.readObject(buffer);
- }
- }
-
- /**
- * Is empty query.
- */
- @SuppressWarnings("serial")
- public static class IsEmpty extends TreeQuery<Boolean> {
-
- }
-
- /**
- * Key set query.
- */
- @SuppressWarnings("serial")
- public static class KeySet extends TreeQuery<Set<String>> {
- }
-
- /**
- * Value set query.
- */
- @SuppressWarnings("serial")
- public static class Values
- extends TreeQuery<Collection<Versioned<byte[]>>> {
- }
-
- /**
- * Entry set query.
- */
- @SuppressWarnings("serial")
- public static class EntrySet
- extends TreeQuery<Set<Map.Entry<String, Versioned<byte[]>>>> {
- }
-
- /**
- * Size query.
- */
- @SuppressWarnings("serial")
- public static class Size extends TreeQuery<Integer> {
- }
-
- /**
- * Clear command.
- */
- @SuppressWarnings("serial")
- public static class Clear
- extends TreeCommand<MapEntryUpdateResult.Status> {
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Change listen.
- */
- @SuppressWarnings("serial")
- public static class Listen implements Command<Void>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
- }
-
- /**
- * Change unlisten.
- */
- @SuppressWarnings("serial")
- public static class Unlisten implements Command<Void>,
- CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer,
- Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
- }
-
- /* Tree map specific commands below */
-
- /**
- * First key query.
- */
- @SuppressWarnings("serial")
- public static class FirstKey<K> extends TreeQuery<K> {
- }
-
- /**
- * Last key query.
- */
- @SuppressWarnings("serial")
- public static class LastKey<K> extends TreeQuery<K> {
- }
-
- /**
- * First entry query.
- */
- @SuppressWarnings("serial")
- public static class FirstEntry<K, V> extends TreeQuery<Map.Entry<K, V>> {
- }
-
- /**
- * Last entry query.
- */
- @SuppressWarnings("serial")
- public static class LastEntry<K, V> extends TreeQuery<Map.Entry<K, V>> {
- }
-
- /**
- * First entry query, if none exists returns null.
- */
- @SuppressWarnings("serial")
- public static class PollFirstEntry<K, V>
- extends TreeQuery<Map.Entry<K, V>> {
- }
-
- /**
- * Last entry query, if none exists returns null.
- */
- @SuppressWarnings("serial")
- public static class PollLastEntry<K, V>
- extends TreeQuery<Map.Entry<K, V>> {
- }
-
- /**
- * Query returns the entry associated with the largest key less than the
- * passed in key.
- */
- @SuppressWarnings("serial")
- public static class LowerEntry<K, V> extends KeyQuery<K> {
- public LowerEntry() {
- }
-
- public LowerEntry(String key) {
- super(key);
- }
- }
-
- /**
- * Query returns the largest key less than the specified key.
- */
- @SuppressWarnings("serial")
- public static class LowerKey<K> extends KeyQuery<K> {
- public LowerKey() {
- }
-
- public LowerKey(String key) {
- super(key);
- }
- }
-
- /**
- * Query returns the entry associated with the largest key smaller than or
- * equal to the specified key.
- */
- @SuppressWarnings("serial")
- public static class FloorEntry<K, V> extends KeyQuery<Map.Entry<K, V>> {
- public FloorEntry() {
- }
-
- public FloorEntry(String key) {
- super(key);
- }
- }
-
- /**
- * Query returns the largest key smaller than or equal to the passed in
- * key.
- */
- @SuppressWarnings("serial")
- public static class FloorKey<K> extends KeyQuery<K> {
- public FloorKey() {
- }
-
- public FloorKey(String key) {
- super(key);
- }
- }
-
- /**
- * Returns the entry associated with the smallest key larger than or equal
- * to the specified key.
- */
- @SuppressWarnings("serial")
- public static class CeilingEntry<K, V> extends KeyQuery<Map.Entry<K, V>> {
- public CeilingEntry() {
- }
-
- public CeilingEntry(String key) {
- super(key);
- }
- }
-
- /**
- * Returns the smallest key larger than or equal to the specified key.
- *
- * @param <K> key type
- */
- @SuppressWarnings("serial")
- public static class CeilingKey<K> extends KeyQuery<K> {
- public CeilingKey() {
- }
-
- public CeilingKey(String key) {
- super(key);
- }
- }
-
- /**
- * Returns the entry associated with the smallest key larger than the
- * specified key.
- */
- @SuppressWarnings("serial")
- public static class HigherEntry<K, V> extends KeyQuery<Map.Entry<K, V>> {
- public HigherEntry() {
- }
-
- public HigherEntry(String key) {
- super(key);
- }
- }
-
- /**
- * Returns the smallest key larger than the specified key.
- */
- @SuppressWarnings("serial")
- public static class HigherKey<K> extends KeyQuery<K> {
- public HigherKey() {
- }
-
- public HigherKey(String key) {
- super(key);
- }
- }
-
- @SuppressWarnings("serial")
- public static class NavigableKeySet<K, V>
- extends TreeQuery<NavigableSet<K>> {
- }
-
- @SuppressWarnings("serial")
- public static class SubMap<K, V> extends TreeQuery<NavigableMap<K, V>> {
- private K fromKey;
- private K toKey;
- private boolean inclusiveFrom;
- private boolean inclusiveTo;
-
- public SubMap() {
- }
-
- public SubMap(K fromKey, K toKey, boolean inclusiveFrom,
- boolean inclusiveTo) {
- this.fromKey = fromKey;
- this.toKey = toKey;
- this.inclusiveFrom = inclusiveFrom;
- this.inclusiveTo = inclusiveTo;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("getFromKey", fromKey)
- .add("getToKey", toKey)
- .add("inclusiveFrotBound", inclusiveFrom)
- .add("inclusiveToBound", inclusiveTo)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> bufferOutput,
- Serializer serializer) {
- super.writeObject(bufferOutput, serializer);
- serializer.writeObject(fromKey, bufferOutput);
- serializer.writeObject(toKey, bufferOutput);
- serializer.writeObject(inclusiveFrom, bufferOutput);
- serializer.writeObject(inclusiveTo, bufferOutput);
- }
-
- @Override
- public void readObject(BufferInput<?> bufferInput,
- Serializer serializer) {
- super.readObject(bufferInput, serializer);
- fromKey = serializer.readObject(bufferInput);
- toKey = serializer.readObject(bufferInput);
- inclusiveFrom = serializer.readObject(bufferInput);
- inclusiveTo = serializer.readObject(bufferInput);
- }
-
- public K fromKey() {
- return fromKey;
- }
-
- public K toKey() {
- return toKey;
- }
-
- public boolean isInclusiveFrom() {
- return inclusiveFrom;
- }
-
- public boolean isInclusiveTo() {
- return inclusiveTo;
- }
- }
-
- /**
- * Tree map command type resolver.
- */
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- //NOTE the registration values must be unique throughout the
- // project.
- registry.register(ContainsKey.class, -1161);
- registry.register(ContainsValue.class, -1162);
- registry.register(Get.class, -1163);
- registry.register(GetOrDefault.class, -1192);
- registry.register(EntrySet.class, -1164);
- registry.register(Values.class, -1165);
- registry.register(KeySet.class, -1166);
- registry.register(Clear.class, -1167);
- registry.register(IsEmpty.class, -1168);
- registry.register(Size.class, -1169);
- registry.register(Listen.class, -1170);
- registry.register(Unlisten.class, -1171);
- //Transaction related commands will be added here with numbers
- // -1172 to -1174
- registry.register(UpdateAndGet.class, -1175);
- registry.register(FirstKey.class, -1176);
- registry.register(LastKey.class, -1177);
- registry.register(FirstEntry.class, -1178);
- registry.register(LastEntry.class, -1179);
- registry.register(PollFirstEntry.class, -1180);
- registry.register(PollLastEntry.class, -1181);
- registry.register(LowerEntry.class, -1182);
- registry.register(LowerKey.class, -1183);
- registry.register(FloorEntry.class, -1184);
- registry.register(FloorKey.class, -1185);
- registry.register(CeilingEntry.class, -1186);
- registry.register(CeilingKey.class, -1187);
- registry.register(HigherEntry.class, -1188);
- registry.register(HigherKey.class, -1189);
- registry.register(SubMap.class, -1190);
- registry.register(NavigableKeySet.class, -1191);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java
new file mode 100644
index 0000000..ab3b972
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.event.EventType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.MapEvent;
+
+/**
+ * Atomix consistent tree map events.
+ */
+public enum AtomixConsistentTreeMapEvents implements EventType {
+ CHANGE("change");
+
+ private final String id;
+
+ AtomixConsistentTreeMapEvents(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50)
+ .register(MapEvent.class)
+ .register(MapEvent.Type.class)
+ .build("AtomixConsistentTreeMapEvents");
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java
deleted file mode 100644
index 8ce8557..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * Factory for {@link AtomixConsistentTreeMap}.
- */
-public class AtomixConsistentTreeMapFactory implements ResourceFactory<AtomixConsistentTreeMap> {
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixConsistentTreeMapCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixConsistentTreeMapState(config);
- }
-
- @Override
- public AtomixConsistentTreeMap createInstance(CopycatClient client, Properties options) {
- return new AtomixConsistentTreeMap(client, options);
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java
new file mode 100644
index 0000000..c6b8d88
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.AbstractMap;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Maps;
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Versioned;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link org.onosproject.store.service.AsyncConsistentTreeMap} Resource
+ * state machine operations.
+ */
+public enum AtomixConsistentTreeMapOperations implements OperationId {
+ CONTAINS_KEY("containsKey", OperationType.QUERY),
+ CONTAINS_VALUE("containsValue", OperationType.QUERY),
+ ENTRY_SET("entrySet", OperationType.QUERY),
+ GET("get", OperationType.QUERY),
+ GET_OR_DEFAULT("getOrDefault", OperationType.QUERY),
+ IS_EMPTY("isEmpty", OperationType.QUERY),
+ KEY_SET("keySet", OperationType.QUERY),
+ SIZE("size", OperationType.QUERY),
+ VALUES("values", OperationType.QUERY),
+ SUB_MAP("subMap", OperationType.QUERY),
+ FIRST_KEY("firstKey", OperationType.QUERY),
+ LAST_KEY("lastKey", OperationType.QUERY),
+ FIRST_ENTRY("firstEntry", OperationType.QUERY),
+ LAST_ENTRY("lastEntry", OperationType.QUERY),
+ POLL_FIRST_ENTRY("pollFirstEntry", OperationType.QUERY),
+ POLL_LAST_ENTRY("pollLastEntry", OperationType.QUERY),
+ LOWER_ENTRY("lowerEntry", OperationType.QUERY),
+ LOWER_KEY("lowerKey", OperationType.QUERY),
+ FLOOR_ENTRY("floorEntry", OperationType.QUERY),
+ FLOOR_KEY("floorKey", OperationType.QUERY),
+ CEILING_ENTRY("ceilingEntry", OperationType.QUERY),
+ CEILING_KEY("ceilingKey", OperationType.QUERY),
+ HIGHER_ENTRY("higherEntry", OperationType.QUERY),
+ HIGHER_KEY("higherKey", OperationType.QUERY),
+ UPDATE_AND_GET("updateAndGet", OperationType.COMMAND),
+ CLEAR("clear", OperationType.COMMAND),
+ ADD_LISTENER("addListener", OperationType.COMMAND),
+ REMOVE_LISTENER("removeListener", OperationType.COMMAND);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixConsistentTreeMapOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(ContainsKey.class)
+ .register(ContainsValue.class)
+ .register(Get.class)
+ .register(GetOrDefault.class)
+ .register(LowerKey.class)
+ .register(LowerEntry.class)
+ .register(HigherKey.class)
+ .register(HigherEntry.class)
+ .register(FloorKey.class)
+ .register(FloorEntry.class)
+ .register(CeilingKey.class)
+ .register(CeilingEntry.class)
+ .register(UpdateAndGet.class)
+ .register(Match.class)
+ .register(Versioned.class)
+ .register(MapEntryUpdateResult.class)
+ .register(MapEntryUpdateResult.Status.class)
+ .register(AbstractMap.SimpleImmutableEntry.class)
+ .register(Maps.immutableEntry("", "").getClass())
+ .build("AtomixConsistentTreeMapOperations");
+
+ /**
+ * Abstract treeMap command.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class TreeOperation {
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .toString();
+ }
+ }
+
+ /**
+ * Abstract key-based query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class KeyOperation extends TreeOperation {
+ protected String key;
+
+ public KeyOperation(String key) {
+ this.key = checkNotNull(key);
+ }
+
+ public KeyOperation() {
+ }
+
+ public String key() {
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .toString();
+ }
+ }
+
+ /**
+ * Abstract value-based query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class ValueOperation extends TreeOperation {
+ protected byte[] value;
+
+ public ValueOperation() {}
+
+ public ValueOperation(byte[] value) {
+ this.value = checkNotNull(value);
+ }
+
+ public byte[] value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("value", value)
+ .toString();
+ }
+ }
+
+ /**
+ * Contains key command.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsKey extends KeyOperation {
+
+ public ContainsKey(String key) {
+ super(key);
+ }
+
+ public ContainsKey() {
+ }
+ }
+ /**
+ * Contains value command.
+ */
+ @SuppressWarnings("serial")
+ public static class ContainsValue extends ValueOperation {
+ public ContainsValue() {
+ }
+
+ public ContainsValue(byte[] value) {
+ super(value);
+ }
+
+ }
+
+ /**
+ * AsyncConsistentTreeMap update command.
+ */
+ @SuppressWarnings("serial")
+ public static class UpdateAndGet extends TreeOperation {
+ private String key;
+ private byte[] value;
+ private Match<byte[]> valueMatch;
+ private Match<Long> versionMatch;
+ public UpdateAndGet() {
+ }
+
+ public UpdateAndGet(String key,
+ byte[] value,
+ Match<byte[]> valueMatch,
+ Match<Long> versionMatch) {
+ this.key = key;
+ this.value = value;
+ this.valueMatch = valueMatch;
+ this.versionMatch = versionMatch;
+ }
+
+ public String key() {
+ return this.key;
+ }
+
+ public byte[] value() {
+ return this.value;
+ }
+
+ public Match<byte[]> valueMatch() {
+ return this.valueMatch;
+ }
+
+ public Match<Long> versionMatch() {
+ return this.versionMatch;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("key", key)
+ .add("value", value)
+ .add("valueMatch", valueMatch)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Get query.
+ */
+ @SuppressWarnings("serial")
+ public static class Get extends KeyOperation {
+ public Get() {
+ }
+
+ public Get(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Get or default query.
+ */
+ @SuppressWarnings("serial")
+ public static class GetOrDefault extends KeyOperation {
+ private byte[] defaultValue;
+
+ public GetOrDefault() {
+ }
+
+ public GetOrDefault(String key, byte[] defaultValue) {
+ super(key);
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Returns the default value.
+ *
+ * @return the default value
+ */
+ public byte[] defaultValue() {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Query returns the entry associated with the largest key less than the
+ * passed in key.
+ */
+ @SuppressWarnings("serial")
+ public static class LowerEntry extends KeyOperation {
+ public LowerEntry() {
+ }
+
+ public LowerEntry(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Query returns the largest key less than the specified key.
+ */
+ @SuppressWarnings("serial")
+ public static class LowerKey extends KeyOperation {
+ public LowerKey() {
+ }
+
+ public LowerKey(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Query returns the entry associated with the largest key smaller than or
+ * equal to the specified key.
+ */
+ @SuppressWarnings("serial")
+ public static class FloorEntry extends KeyOperation {
+ public FloorEntry() {
+ }
+
+ public FloorEntry(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Query returns the largest key smaller than or equal to the passed in
+ * key.
+ */
+ @SuppressWarnings("serial")
+ public static class FloorKey extends KeyOperation {
+ public FloorKey() {
+ }
+
+ public FloorKey(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Returns the entry associated with the smallest key larger than or equal
+ * to the specified key.
+ */
+ @SuppressWarnings("serial")
+ public static class CeilingEntry extends KeyOperation {
+ public CeilingEntry() {
+ }
+
+ public CeilingEntry(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Returns the smallest key larger than or equal to the specified key.
+ */
+ @SuppressWarnings("serial")
+ public static class CeilingKey extends KeyOperation {
+ public CeilingKey() {
+ }
+
+ public CeilingKey(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Returns the entry associated with the smallest key larger than the
+ * specified key.
+ */
+ @SuppressWarnings("serial")
+ public static class HigherEntry extends KeyOperation {
+ public HigherEntry() {
+ }
+
+ public HigherEntry(String key) {
+ super(key);
+ }
+ }
+
+ /**
+ * Returns the smallest key larger than the specified key.
+ */
+ @SuppressWarnings("serial")
+ public static class HigherKey extends KeyOperation {
+ public HigherKey() {
+ }
+
+ public HigherKey(String key) {
+ super(key);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class SubMap<K, V> extends TreeOperation {
+ private K fromKey;
+ private K toKey;
+ private boolean inclusiveFrom;
+ private boolean inclusiveTo;
+
+ public SubMap() {
+ }
+
+ public SubMap(K fromKey, K toKey, boolean inclusiveFrom,
+ boolean inclusiveTo) {
+ this.fromKey = fromKey;
+ this.toKey = toKey;
+ this.inclusiveFrom = inclusiveFrom;
+ this.inclusiveTo = inclusiveTo;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("getFromKey", fromKey)
+ .add("getToKey", toKey)
+ .add("inclusiveFrotBound", inclusiveFrom)
+ .add("inclusiveToBound", inclusiveTo)
+ .toString();
+ }
+
+ public K fromKey() {
+ return fromKey;
+ }
+
+ public K toKey() {
+ return toKey;
+ }
+
+ public boolean isInclusiveFrom() {
+ return inclusiveFrom;
+ }
+
+ public boolean isInclusiveTo() {
+ return inclusiveTo;
+ }
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java
new file mode 100644
index 0000000..b9e7135
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.session.RaftSession;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Versioned;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_VALUE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsValue;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ENTRY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET_OR_DEFAULT;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.Get;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GetOrDefault;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.IS_EMPTY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.KEY_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_KEY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerEntry;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerKey;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_FIRST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_LAST_ENTRY;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SIZE;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SUB_MAP;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SubMap;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UPDATE_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UpdateAndGet;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.VALUES;
+import static org.onosproject.store.primitives.resources.impl.MapEntryUpdateResult.Status;
+
+/**
+ * State machine corresponding to {@link AtomixConsistentTreeMap} backed by a
+ * {@link TreeMap}.
+ */
+public class AtomixConsistentTreeMapService extends AbstractRaftService {
+
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixConsistentTreeMapOperations.NAMESPACE)
+ .register(AtomixConsistentTreeMapEvents.NAMESPACE)
+ .register(TreeMapEntryValue.class)
+ .register(new HashMap<>().keySet().getClass())
+ .register(TreeMap.class)
+ .build());
+
+ private final Map<Long, RaftSession> listeners = Maps.newHashMap();
+ private TreeMap<String, TreeMapEntryValue> tree = Maps.newTreeMap();
+ private final Set<String> preparedKeys = Sets.newHashSet();
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeObject(Sets.newHashSet(listeners.keySet()), SERIALIZER::encode);
+ writer.writeObject(preparedKeys, SERIALIZER::encode);
+ writer.writeObject(tree, SERIALIZER::encode);
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ listeners.clear();
+ for (long sessionId : reader.<Set<Long>>readObject(SERIALIZER::decode)) {
+ listeners.put(sessionId, getSessions().getSession(sessionId));
+ }
+
+ preparedKeys.clear();
+ preparedKeys.addAll(reader.readObject(SERIALIZER::decode));
+
+ tree.clear();
+ tree.putAll(reader.readObject(SERIALIZER::decode));
+ }
+
+ @Override
+ public void configure(RaftServiceExecutor executor) {
+ // Listeners
+ executor.register(ADD_LISTENER, this::listen);
+ executor.register(REMOVE_LISTENER, this::unlisten);
+ // Queries
+ executor.register(CONTAINS_KEY, SERIALIZER::decode, this::containsKey, SERIALIZER::encode);
+ executor.register(CONTAINS_VALUE, SERIALIZER::decode, this::containsValue, SERIALIZER::encode);
+ executor.register(ENTRY_SET, this::entrySet, SERIALIZER::encode);
+ executor.register(GET, SERIALIZER::decode, this::get, SERIALIZER::encode);
+ executor.register(GET_OR_DEFAULT, SERIALIZER::decode, this::getOrDefault, SERIALIZER::encode);
+ executor.register(IS_EMPTY, this::isEmpty, SERIALIZER::encode);
+ executor.register(KEY_SET, this::keySet, SERIALIZER::encode);
+ executor.register(SIZE, this::size, SERIALIZER::encode);
+ executor.register(VALUES, this::values, SERIALIZER::encode);
+ executor.register(SUB_MAP, SERIALIZER::decode, this::subMap, SERIALIZER::encode);
+ executor.register(FIRST_KEY, this::firstKey, SERIALIZER::encode);
+ executor.register(LAST_KEY, this::lastKey, SERIALIZER::encode);
+ executor.register(FIRST_ENTRY, this::firstEntry, SERIALIZER::encode);
+ executor.register(LAST_ENTRY, this::lastEntry, SERIALIZER::encode);
+ executor.register(POLL_FIRST_ENTRY, this::pollFirstEntry, SERIALIZER::encode);
+ executor.register(POLL_LAST_ENTRY, this::pollLastEntry, SERIALIZER::encode);
+ executor.register(LOWER_ENTRY, SERIALIZER::decode, this::lowerEntry, SERIALIZER::encode);
+ executor.register(LOWER_KEY, SERIALIZER::decode, this::lowerKey, SERIALIZER::encode);
+ executor.register(FLOOR_ENTRY, SERIALIZER::decode, this::floorEntry, SERIALIZER::encode);
+ executor.register(FLOOR_KEY, SERIALIZER::decode, this::floorKey, SERIALIZER::encode);
+ executor.register(CEILING_ENTRY, SERIALIZER::decode, this::ceilingEntry, SERIALIZER::encode);
+ executor.register(CEILING_KEY, SERIALIZER::decode, this::ceilingKey, SERIALIZER::encode);
+ executor.register(HIGHER_ENTRY, SERIALIZER::decode, this::higherEntry, SERIALIZER::encode);
+ executor.register(HIGHER_KEY, SERIALIZER::decode, this::higherKey, SERIALIZER::encode);
+
+ // Commands
+ executor.register(UPDATE_AND_GET, SERIALIZER::decode, this::updateAndGet, SERIALIZER::encode);
+ executor.register(CLEAR, this::clear, SERIALIZER::encode);
+ }
+
+ protected boolean containsKey(Commit<? extends ContainsKey> commit) {
+ return toVersioned(tree.get((commit.value().key()))) != null;
+ }
+
+ protected boolean containsValue(Commit<? extends ContainsValue> commit) {
+ Match<byte[]> valueMatch = Match
+ .ifValue(commit.value().value());
+ return tree.values().stream().anyMatch(
+ value -> valueMatch.matches(value.value()));
+ }
+
+ protected Versioned<byte[]> get(Commit<? extends Get> commit) {
+ return toVersioned(tree.get(commit.value().key()));
+ }
+
+ protected Versioned<byte[]> getOrDefault(Commit<? extends GetOrDefault> commit) {
+ Versioned<byte[]> value = toVersioned(tree.get(commit.value().key()));
+ return value != null ? value : new Versioned<>(commit.value().defaultValue(), 0);
+ }
+
+ protected int size(Commit<Void> commit) {
+ return tree.size();
+ }
+
+ protected boolean isEmpty(Commit<Void> commit) {
+ return tree.isEmpty();
+ }
+
+ protected Set<String> keySet(Commit<Void> commit) {
+ return tree.keySet().stream().collect(Collectors.toSet());
+ }
+
+ protected Collection<Versioned<byte[]>> values(Commit<Void> commit) {
+ return tree.values().stream().map(this::toVersioned)
+ .collect(Collectors.toList());
+ }
+
+ protected Set<Map.Entry<String, Versioned<byte[]>>> entrySet(Commit<Void> commit) {
+ return tree
+ .entrySet()
+ .stream()
+ .map(e -> Maps.immutableEntry(e.getKey(),
+ toVersioned(e.getValue())))
+ .collect(Collectors.toSet());
+ }
+
+ protected MapEntryUpdateResult<String, byte[]> updateAndGet(Commit<? extends UpdateAndGet> commit) {
+ Status updateStatus = validate(commit.value());
+ String key = commit.value().key();
+ TreeMapEntryValue oldCommitValue = tree.get(commit.value().key());
+ Versioned<byte[]> oldTreeValue = toVersioned(oldCommitValue);
+
+ if (updateStatus != Status.OK) {
+ return new MapEntryUpdateResult<>(updateStatus, "", key,
+ oldTreeValue, oldTreeValue);
+ }
+
+ byte[] newValue = commit.value().value();
+ long newVersion = commit.index();
+ Versioned<byte[]> newTreeValue = newValue == null ? null
+ : new Versioned<byte[]>(newValue, newVersion);
+
+ MapEvent.Type updateType = newValue == null ? MapEvent.Type.REMOVE
+ : oldCommitValue == null ? MapEvent.Type.INSERT :
+ MapEvent.Type.UPDATE;
+ if (updateType == MapEvent.Type.REMOVE ||
+ updateType == MapEvent.Type.UPDATE) {
+ tree.remove(key);
+ }
+ if (updateType == MapEvent.Type.INSERT ||
+ updateType == MapEvent.Type.UPDATE) {
+ tree.put(key, new TreeMapEntryValue(newVersion, commit.value().value()));
+ }
+ publish(Lists.newArrayList(new MapEvent<>("", key, newTreeValue,
+ oldTreeValue)));
+ return new MapEntryUpdateResult<>(updateStatus, "", key, oldTreeValue,
+ newTreeValue);
+ }
+
+ protected Status clear(Commit<Void> commit) {
+ Iterator<Map.Entry<String, TreeMapEntryValue>> iterator = tree
+ .entrySet()
+ .iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, TreeMapEntryValue> entry = iterator.next();
+ String key = entry.getKey();
+ TreeMapEntryValue value = entry.getValue();
+ Versioned<byte[]> removedValue =
+ new Versioned<byte[]>(value.value(),
+ value.version());
+ publish(Lists.newArrayList(new MapEvent<>("", key, null,
+ removedValue)));
+ iterator.remove();
+ }
+ return Status.OK;
+ }
+
+ protected void listen(Commit<Void> commit) {
+ listeners.put(commit.session().sessionId().id(), commit.session());
+ }
+
+ protected void unlisten(Commit<Void> commit) {
+ closeListener(commit.session().sessionId().id());
+ }
+
+ private Status validate(UpdateAndGet update) {
+ TreeMapEntryValue existingValue = tree.get(update.key());
+ if (existingValue == null && update.value() == null) {
+ return Status.NOOP;
+ }
+ if (preparedKeys.contains(update.key())) {
+ return Status.WRITE_LOCK;
+ }
+ byte[] existingRawValue = existingValue == null ? null :
+ existingValue.value();
+ Long existingVersion = existingValue == null ? null :
+ existingValue.version();
+ return update.valueMatch().matches(existingRawValue)
+ && update.versionMatch().matches(existingVersion) ?
+ Status.OK
+ : Status.PRECONDITION_FAILED;
+ }
+
+ protected NavigableMap<String, TreeMapEntryValue> subMap(
+ Commit<? extends SubMap> commit) {
+ // Do not support this until lazy communication is possible. At present
+ // it transmits up to the entire map.
+ SubMap<String, TreeMapEntryValue> subMap = commit.value();
+ return tree.subMap(subMap.fromKey(), subMap.isInclusiveFrom(),
+ subMap.toKey(), subMap.isInclusiveTo());
+ }
+
+ protected String firstKey(Commit<Void> commit) {
+ if (tree.isEmpty()) {
+ return null;
+ }
+ return tree.firstKey();
+ }
+
+ protected String lastKey(Commit<Void> commit) {
+ return tree.isEmpty() ? null : tree.lastKey();
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> higherEntry(Commit<? extends HigherEntry> commit) {
+ if (tree.isEmpty()) {
+ return null;
+ }
+ return toVersionedEntry(
+ tree.higherEntry(commit.value().key()));
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> firstEntry(Commit<Void> commit) {
+ if (tree.isEmpty()) {
+ return null;
+ }
+ return toVersionedEntry(tree.firstEntry());
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> lastEntry(Commit<Void> commit) {
+ if (tree.isEmpty()) {
+ return null;
+ }
+ return toVersionedEntry(tree.lastEntry());
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> pollFirstEntry(Commit<Void> commit) {
+ return toVersionedEntry(tree.pollFirstEntry());
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> pollLastEntry(Commit<Void> commit) {
+ return toVersionedEntry(tree.pollLastEntry());
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> lowerEntry(Commit<? extends LowerEntry> commit) {
+ return toVersionedEntry(tree.lowerEntry(commit.value().key()));
+ }
+
+ protected String lowerKey(Commit<? extends LowerKey> commit) {
+ return tree.lowerKey(commit.value().key());
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> floorEntry(Commit<? extends FloorEntry> commit) {
+ return toVersionedEntry(tree.floorEntry(commit.value().key()));
+ }
+
+ protected String floorKey(Commit<? extends FloorKey> commit) {
+ return tree.floorKey(commit.value().key());
+ }
+
+ protected Map.Entry<String, Versioned<byte[]>> ceilingEntry(Commit<CeilingEntry> commit) {
+ return toVersionedEntry(
+ tree.ceilingEntry(commit.value().key()));
+ }
+
+ protected String ceilingKey(Commit<CeilingKey> commit) {
+ return tree.ceilingKey(commit.value().key());
+ }
+
+ protected String higherKey(Commit<HigherKey> commit) {
+ return tree.higherKey(commit.value().key());
+ }
+
+ private Versioned<byte[]> toVersioned(TreeMapEntryValue value) {
+ return value == null ? null :
+ new Versioned<byte[]>(value.value(), value.version());
+ }
+
+ private Map.Entry<String, Versioned<byte[]>> toVersionedEntry(
+ Map.Entry<String, TreeMapEntryValue> entry) {
+ //FIXME is this the best type of entry to return?
+ return entry == null ? null : new SimpleImmutableEntry<>(
+ entry.getKey(), toVersioned(entry.getValue()));
+ }
+
+ private void publish(List<MapEvent<String, byte[]>> events) {
+ listeners.values().forEach(session -> session.publish(CHANGE, SERIALIZER::encode, events));
+ }
+
+ @Override
+ public void onExpire(RaftSession session) {
+ closeListener(session.sessionId().id());
+ }
+
+ @Override
+ public void onClose(RaftSession session) {
+ closeListener(session.sessionId().id());
+ }
+
+ private void closeListener(Long sessionId) {
+ listeners.remove(sessionId);
+ }
+
+ private static class TreeMapEntryValue {
+ private final long version;
+ private final byte[] value;
+
+ public TreeMapEntryValue(long version, byte[] value) {
+ this.version = version;
+ this.value = value;
+ }
+
+ public byte[] value() {
+ return value;
+ }
+
+ public long version() {
+ return version;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java
deleted file mode 100644
index 576bfea..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.session.ServerSession;
-import io.atomix.copycat.server.session.SessionListener;
-import io.atomix.resource.ResourceStateMachine;
-import org.onlab.util.Match;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.Versioned;
-
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Clear;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsValue;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.EntrySet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Get;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.GetOrDefault;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.IsEmpty;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.KeySet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Listen;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerKey;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollFirstEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollLastEntry;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Size;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.SubMap;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Unlisten;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.UpdateAndGet;
-import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Values;
-import static org.onosproject.store.primitives.resources.impl.MapEntryUpdateResult.*;
-
-/**
- * State machine corresponding to {@link AtomixConsistentTreeMap} backed by a
- * {@link TreeMap}.
- */
-public class AtomixConsistentTreeMapState extends ResourceStateMachine implements SessionListener {
-
- private final Map<Long, Commit<? extends Listen>> listeners =
- Maps.newHashMap();
- private TreeMap<String, TreeMapEntryValue> tree = Maps.newTreeMap();
- private final Set<String> preparedKeys = Sets.newHashSet();
-
- private Function<Commit<SubMap>, NavigableMap<String, TreeMapEntryValue>> subMapFunction = this::subMap;
- private Function<Commit<FirstKey>, String> firstKeyFunction = this::firstKey;
- private Function<Commit<LastKey>, String> lastKeyFunction = this::lastKey;
- private Function<Commit<HigherEntry>, Map.Entry<String, Versioned<byte[]>>> higherEntryFunction =
- this::higherEntry;
- private Function<Commit<FirstEntry>, Map.Entry<String, Versioned<byte[]>>> firstEntryFunction =
- this::firstEntry;
- private Function<Commit<LastEntry>, Map.Entry<String, Versioned<byte[]>>> lastEntryFunction =
- this::lastEntry;
- private Function<Commit<PollFirstEntry>, Map.Entry<String, Versioned<byte[]>>> pollFirstEntryFunction =
- this::pollFirstEntry;
- private Function<Commit<PollLastEntry>, Map.Entry<String, Versioned<byte[]>>> pollLastEntryFunction =
- this::pollLastEntry;
- private Function<Commit<LowerEntry>, Map.Entry<String, Versioned<byte[]>>> lowerEntryFunction =
- this::lowerEntry;
- private Function<Commit<LowerKey>, String> lowerKeyFunction = this::lowerKey;
- private Function<Commit<FloorEntry>, Map.Entry<String, Versioned<byte[]>>> floorEntryFunction =
- this::floorEntry;
- private Function<Commit<CeilingEntry>, Map.Entry<String, Versioned<byte[]>>> ceilingEntryFunction =
- this::ceilingEntry;
- private Function<Commit<FloorKey>, String> floorKeyFunction = this::floorKey;
- private Function<Commit<CeilingKey>, String> ceilingKeyFunction = this::ceilingKey;
- private Function<Commit<HigherKey>, String> higherKeyFunction = this::higherKey;
-
- public AtomixConsistentTreeMapState(Properties properties) {
- super(properties);
- }
-
- @Override
- public void configure(StateMachineExecutor executor) {
- // Listeners
- executor.register(Listen.class, this::listen);
- executor.register(Unlisten.class, this::unlisten);
- // Queries
- executor.register(ContainsKey.class, this::containsKey);
- executor.register(ContainsValue.class, this::containsValue);
- executor.register(EntrySet.class, this::entrySet);
- executor.register(Get.class, this::get);
- executor.register(GetOrDefault.class, this::getOrDefault);
- executor.register(IsEmpty.class, this::isEmpty);
- executor.register(KeySet.class, this::keySet);
- executor.register(Size.class, this::size);
- executor.register(Values.class, this::values);
- executor.register(SubMap.class, subMapFunction);
- executor.register(FirstKey.class, firstKeyFunction);
- executor.register(LastKey.class, lastKeyFunction);
- executor.register(FirstEntry.class, firstEntryFunction);
- executor.register(LastEntry.class, lastEntryFunction);
- executor.register(PollFirstEntry.class, pollFirstEntryFunction);
- executor.register(PollLastEntry.class, pollLastEntryFunction);
- executor.register(LowerEntry.class, lowerEntryFunction);
- executor.register(LowerKey.class, lowerKeyFunction);
- executor.register(FloorEntry.class, floorEntryFunction);
- executor.register(FloorKey.class, floorKeyFunction);
- executor.register(CeilingEntry.class, ceilingEntryFunction);
- executor.register(CeilingKey.class, ceilingKeyFunction);
- executor.register(HigherEntry.class, higherEntryFunction);
- executor.register(HigherKey.class, higherKeyFunction);
-
- // Commands
- executor.register(UpdateAndGet.class, this::updateAndGet);
- executor.register(Clear.class, this::clear);
- }
-
- @Override
- public void delete() {
- listeners.values().forEach(Commit::close);
- listeners.clear();
- tree.values().forEach(TreeMapEntryValue::discard);
- tree.clear();
- }
-
- protected boolean containsKey(Commit<? extends ContainsKey> commit) {
- try {
- return toVersioned(tree.get((commit.operation().key()))) != null;
- } finally {
- commit.close();
- }
- }
-
- protected boolean containsValue(Commit<? extends ContainsValue> commit) {
- try {
- Match<byte[]> valueMatch = Match
- .ifValue(commit.operation().value());
- return tree.values().stream().anyMatch(
- value -> valueMatch.matches(value.value()));
- } finally {
- commit.close();
- }
- }
-
- protected Versioned<byte[]> get(Commit<? extends Get> commit) {
- try {
- return toVersioned(tree.get(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- protected Versioned<byte[]> getOrDefault(Commit<? extends GetOrDefault> commit) {
- try {
- Versioned<byte[]> value = toVersioned(tree.get(commit.operation().key()));
- return value != null ? value : new Versioned<>(commit.operation().defaultValue(), 0);
- } finally {
- commit.close();
- }
- }
-
- protected int size(Commit<? extends Size> commit) {
- try {
- return tree.size();
- } finally {
- commit.close();
- }
- }
-
- protected boolean isEmpty(Commit<? extends IsEmpty> commit) {
- try {
- return tree.isEmpty();
- } finally {
- commit.close();
- }
- }
-
- protected Set<String> keySet(Commit<? extends KeySet> commit) {
- try {
- return tree.keySet().stream().collect(Collectors.toSet());
- } finally {
- commit.close();
- }
- }
-
- protected Collection<Versioned<byte[]>> values(
- Commit<? extends Values> commit) {
- try {
- return tree.values().stream().map(this::toVersioned)
- .collect(Collectors.toList());
- } finally {
- commit.close();
- }
- }
-
- protected Set<Map.Entry<String, Versioned<byte[]>>> entrySet(
- Commit<? extends EntrySet> commit) {
- try {
- return tree
- .entrySet()
- .stream()
- .map(e -> Maps.immutableEntry(e.getKey(),
- toVersioned(e.getValue())))
- .collect(Collectors.toSet());
- } finally {
- commit.close();
- }
- }
-
- protected MapEntryUpdateResult<String, byte[]> updateAndGet(
- Commit<? extends UpdateAndGet> commit) {
- Status updateStatus = validate(commit.operation());
- String key = commit.operation().key();
- TreeMapEntryValue oldCommitValue = tree.get(commit.operation().key());
- Versioned<byte[]> oldTreeValue = toVersioned(oldCommitValue);
-
- if (updateStatus != Status.OK) {
- commit.close();
- return new MapEntryUpdateResult<>(updateStatus, "", key,
- oldTreeValue, oldTreeValue);
- }
-
- byte[] newValue = commit.operation().value();
- long newVersion = commit.index();
- Versioned<byte[]> newTreeValue = newValue == null ? null
- : new Versioned<byte[]>(newValue, newVersion);
-
- MapEvent.Type updateType = newValue == null ? MapEvent.Type.REMOVE
- : oldCommitValue == null ? MapEvent.Type.INSERT :
- MapEvent.Type.UPDATE;
- if (updateType == MapEvent.Type.REMOVE ||
- updateType == MapEvent.Type.UPDATE) {
- tree.remove(key);
- oldCommitValue.discard();
- }
- if (updateType == MapEvent.Type.INSERT ||
- updateType == MapEvent.Type.UPDATE) {
- tree.put(key, new NonTransactionalCommit(newVersion, commit));
- } else {
- commit.close();
- }
- publish(Lists.newArrayList(new MapEvent<>("", key, newTreeValue,
- oldTreeValue)));
- return new MapEntryUpdateResult<>(updateStatus, "", key, oldTreeValue,
- newTreeValue);
- }
-
- protected Status clear(
- Commit<? extends Clear> commit) {
- try {
- Iterator<Map.Entry<String, TreeMapEntryValue>> iterator = tree
- .entrySet()
- .iterator();
- while (iterator.hasNext()) {
- Map.Entry<String, TreeMapEntryValue> entry = iterator.next();
- String key = entry.getKey();
- TreeMapEntryValue value = entry.getValue();
- Versioned<byte[]> removedValue =
- new Versioned<byte[]>(value.value(),
- value.version());
- publish(Lists.newArrayList(new MapEvent<>("", key, null,
- removedValue)));
- value.discard();
- iterator.remove();
- }
- return Status.OK;
- } finally {
- commit.close();
- }
- }
-
- protected void listen(
- Commit<? extends Listen> commit) {
- Long sessionId = commit.session().id();
- listeners.put(sessionId, commit);
- commit.session()
- .onStateChange(
- state -> {
- if (state == ServerSession.State.CLOSED
- || state == ServerSession.State.EXPIRED) {
- Commit<? extends Listen> listener =
- listeners.remove(sessionId);
- if (listener != null) {
- listener.close();
- }
- }
- });
- }
-
- protected void unlisten(
- Commit<? extends Unlisten> commit) {
- try {
- Commit<? extends AtomixConsistentTreeMapCommands.Listen> listener =
- listeners.remove(commit.session().id());
- if (listener != null) {
- listener.close();
- }
- } finally {
- commit.close();
- }
- }
-
- private Status validate(UpdateAndGet update) {
- TreeMapEntryValue existingValue = tree.get(update.key());
- if (existingValue == null && update.value() == null) {
- return Status.NOOP;
- }
- if (preparedKeys.contains(update.key())) {
- return Status.WRITE_LOCK;
- }
- byte[] existingRawValue = existingValue == null ? null :
- existingValue.value();
- Long existingVersion = existingValue == null ? null :
- existingValue.version();
- return update.valueMatch().matches(existingRawValue)
- && update.versionMatch().matches(existingVersion) ?
- Status.OK
- : Status.PRECONDITION_FAILED;
- }
-
- protected NavigableMap<String, TreeMapEntryValue> subMap(
- Commit<? extends SubMap> commit) {
- //Do not support this until lazy communication is possible. At present
- // it transmits up to the entire map.
- try {
- SubMap<String, TreeMapEntryValue> subMap = commit.operation();
- return tree.subMap(subMap.fromKey(), subMap.isInclusiveFrom(),
- subMap.toKey(), subMap.isInclusiveTo());
- } finally {
- commit.close();
- }
- }
-
- protected String firstKey(Commit<? extends FirstKey> commit) {
- try {
- if (tree.isEmpty()) {
- return null;
- }
- return tree.firstKey();
- } finally {
- commit.close();
- }
- }
-
- protected String lastKey(Commit<? extends LastKey> commit) {
- try {
- return tree.isEmpty() ? null : tree.lastKey();
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> higherEntry(
- Commit<? extends HigherEntry> commit) {
- try {
- if (tree.isEmpty()) {
- return null;
- }
- return toVersionedEntry(
- tree.higherEntry(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> firstEntry(
- Commit<? extends FirstEntry> commit) {
- try {
- if (tree.isEmpty()) {
- return null;
- }
- return toVersionedEntry(tree.firstEntry());
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> lastEntry(
- Commit<? extends LastEntry> commit) {
- try {
- if (tree.isEmpty()) {
- return null;
- }
- return toVersionedEntry(tree.lastEntry());
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> pollFirstEntry(
- Commit<? extends PollFirstEntry> commit) {
- try {
- return toVersionedEntry(tree.pollFirstEntry());
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> pollLastEntry(
- Commit<? extends PollLastEntry> commit) {
- try {
- return toVersionedEntry(tree.pollLastEntry());
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> lowerEntry(
- Commit<? extends LowerEntry> commit) {
- try {
- return toVersionedEntry(tree.lowerEntry(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- protected String lowerKey(Commit<? extends LowerKey> commit) {
- try {
- return tree.lowerKey(commit.operation().key());
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> floorEntry(
- Commit<? extends FloorEntry> commit) {
- try {
- return toVersionedEntry(tree.floorEntry(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- protected String floorKey(Commit<? extends FloorKey> commit) {
- try {
- return tree.floorKey(commit.operation().key());
- } finally {
- commit.close();
- }
- }
-
- protected Map.Entry<String, Versioned<byte[]>> ceilingEntry(
- Commit<CeilingEntry> commit) {
- try {
- return toVersionedEntry(
- tree.ceilingEntry(commit.operation().key()));
- } finally {
- commit.close();
- }
- }
-
- protected String ceilingKey(Commit<CeilingKey> commit) {
- try {
- return tree.ceilingKey(commit.operation().key());
- } finally {
- commit.close();
- }
- }
-
- protected String higherKey(Commit<HigherKey> commit) {
- try {
- return tree.higherKey(commit.operation().key());
- } finally {
- commit.close();
- }
- }
-
- private Versioned<byte[]> toVersioned(TreeMapEntryValue value) {
- return value == null ? null :
- new Versioned<byte[]>(value.value(), value.version());
- }
-
- private Map.Entry<String, Versioned<byte[]>> toVersionedEntry(
- Map.Entry<String, TreeMapEntryValue> entry) {
- //FIXME is this the best type of entry to return?
- return entry == null ? null : new SimpleImmutableEntry<>(
- entry.getKey(), toVersioned(entry.getValue()));
- }
-
- private void publish(List<MapEvent<String, byte[]>> events) {
- listeners.values().forEach(commit -> commit.session()
- .publish(AtomixConsistentTreeMap.CHANGE_SUBJECT, events));
- }
-
- @Override
- public void register(ServerSession session) {
- }
-
- @Override
- public void unregister(ServerSession session) {
- closeListener(session.id());
- }
-
- @Override
- public void expire(ServerSession session) {
- closeListener(session.id());
- }
-
- @Override
- public void close(ServerSession session) {
- closeListener(session.id());
- }
-
- private void closeListener(Long sessionId) {
- Commit<? extends Listen> commit = listeners.remove(sessionId);
- if (commit != null) {
- commit.close();
- }
- }
-
- private interface TreeMapEntryValue {
-
- byte[] value();
-
- long version();
-
- void discard();
- }
-
- private class NonTransactionalCommit implements TreeMapEntryValue {
- private final long version;
- private final Commit<? extends UpdateAndGet> commit;
-
- public NonTransactionalCommit(long version,
- Commit<? extends UpdateAndGet> commit) {
- this.version = version;
- this.commit = commit;
- }
-
- @Override
- public byte[] value() {
- return commit.operation().value();
- }
-
- @Override
- public long version() {
- return version;
- }
-
- @Override
- public void discard() {
- commit.close();
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java
index e670d40..954008c 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java
@@ -15,63 +15,76 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.variables.DistributedLong;
-
import java.util.concurrent.CompletableFuture;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.AsyncAtomicCounter;
+import org.onosproject.store.service.Serializer;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.ADD_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.AddAndGet;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.COMPARE_AND_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.CompareAndSet;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_INCREMENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GetAndAdd;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.INCREMENT_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.Set;
/**
- * {@code AsyncAtomicCounter} implementation backed by Atomix
- * {@link DistributedLong}.
+ * Atomix counter implementation.
*/
-public class AtomixCounter implements AsyncAtomicCounter {
+public class AtomixCounter extends AbstractRaftPrimitive implements AsyncAtomicCounter {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixCounterOperations.NAMESPACE)
+ .build());
- private final String name;
- private final DistributedLong distLong;
-
- public AtomixCounter(String name, DistributedLong distLong) {
- this.name = name;
- this.distLong = distLong;
+ public AtomixCounter(RaftProxy proxy) {
+ super(proxy);
}
- @Override
- public String name() {
- return name;
- }
-
- @Override
- public CompletableFuture<Long> incrementAndGet() {
- return distLong.incrementAndGet();
- }
-
- @Override
- public CompletableFuture<Long> getAndIncrement() {
- return distLong.getAndIncrement();
- }
-
- @Override
- public CompletableFuture<Long> getAndAdd(long delta) {
- return distLong.getAndAdd(delta);
- }
-
- @Override
- public CompletableFuture<Long> addAndGet(long delta) {
- return distLong.addAndGet(delta);
+ private long nullOrZero(Long value) {
+ return value != null ? value : 0;
}
@Override
public CompletableFuture<Long> get() {
- return distLong.get();
+ return proxy.<Long>invoke(GET, SERIALIZER::decode).thenApply(this::nullOrZero);
}
@Override
public CompletableFuture<Void> set(long value) {
- return distLong.set(value);
+ return proxy.invoke(SET, SERIALIZER::encode, new Set(value));
}
@Override
public CompletableFuture<Boolean> compareAndSet(long expectedValue, long updateValue) {
- return distLong.compareAndSet(expectedValue, updateValue);
+ return proxy.invoke(COMPARE_AND_SET, SERIALIZER::encode,
+ new CompareAndSet(expectedValue, updateValue), SERIALIZER::decode);
+ }
+
+ @Override
+ public CompletableFuture<Long> addAndGet(long delta) {
+ return proxy.invoke(ADD_AND_GET, SERIALIZER::encode, new AddAndGet(delta), SERIALIZER::decode);
+ }
+
+ @Override
+ public CompletableFuture<Long> getAndAdd(long delta) {
+ return proxy.invoke(GET_AND_ADD, SERIALIZER::encode, new GetAndAdd(delta), SERIALIZER::decode);
+ }
+
+ @Override
+ public CompletableFuture<Long> incrementAndGet() {
+ return proxy.invoke(INCREMENT_AND_GET, SERIALIZER::decode);
+ }
+
+ @Override
+ public CompletableFuture<Long> getAndIncrement() {
+ return proxy.invoke(GET_AND_INCREMENT, SERIALIZER::decode);
}
}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java
new file mode 100644
index 0000000..0ebe7e4
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Counter commands.
+ */
+public enum AtomixCounterOperations implements OperationId {
+ SET("set", OperationType.COMMAND),
+ COMPARE_AND_SET("compareAndSet", OperationType.COMMAND),
+ INCREMENT_AND_GET("incrementAndGet", OperationType.COMMAND),
+ GET_AND_INCREMENT("getAndIncrement", OperationType.COMMAND),
+ ADD_AND_GET("addAndGet", OperationType.COMMAND),
+ GET_AND_ADD("getAndAdd", OperationType.COMMAND),
+ GET("get", OperationType.QUERY);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixCounterOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(Get.class)
+ .register(Set.class)
+ .register(CompareAndSet.class)
+ .register(AddAndGet.class)
+ .register(GetAndAdd.class)
+ .build("AtomixCounterOperations");
+
+ /**
+ * Abstract value command.
+ */
+ public abstract static class ValueOperation {
+ }
+
+ /**
+ * Get query.
+ */
+ public static class Get extends ValueOperation {
+ }
+
+ /**
+ * Set command.
+ */
+ public static class Set extends ValueOperation {
+ private Long value;
+
+ public Set() {
+ }
+
+ public Set(Long value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the command value.
+ *
+ * @return The command value.
+ */
+ public Long value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[value=%s]", getClass().getSimpleName(), value);
+ }
+ }
+
+ /**
+ * Compare and set command.
+ */
+ public static class CompareAndSet extends ValueOperation {
+ private Long expect;
+ private Long update;
+
+ public CompareAndSet() {
+ }
+
+ public CompareAndSet(Long expect, Long update) {
+ this.expect = expect;
+ this.update = update;
+ }
+
+ /**
+ * Returns the expected value.
+ *
+ * @return The expected value.
+ */
+ public Long expect() {
+ return expect;
+ }
+
+ /**
+ * Returns the updated value.
+ *
+ * @return The updated value.
+ */
+ public Long update() {
+ return update;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[expect=%s, update=%s]", getClass().getSimpleName(), expect, update);
+ }
+ }
+
+ /**
+ * Delta command.
+ */
+ public abstract static class DeltaOperation extends ValueOperation {
+ private long delta;
+
+ public DeltaOperation() {
+ }
+
+ public DeltaOperation(long delta) {
+ this.delta = delta;
+ }
+
+ /**
+ * Returns the delta.
+ *
+ * @return The delta.
+ */
+ public long delta() {
+ return delta;
+ }
+ }
+
+ /**
+ * Get and add command.
+ */
+ public static class GetAndAdd extends DeltaOperation {
+ public GetAndAdd() {
+ }
+
+ public GetAndAdd(long delta) {
+ super(delta);
+ }
+ }
+
+ /**
+ * Add and get command.
+ */
+ public static class AddAndGet extends DeltaOperation {
+ public AddAndGet() {
+ }
+
+ public AddAndGet(long delta) {
+ super(delta);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java
new file mode 100644
index 0000000..a802393
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Objects;
+
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.ADD_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.AddAndGet;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.COMPARE_AND_SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.CompareAndSet;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_INCREMENT;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GetAndAdd;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.INCREMENT_AND_GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.SET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.Set;
+
+/**
+ * Atomix long state.
+ */
+public class AtomixCounterService extends AbstractRaftService {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixCounterOperations.NAMESPACE)
+ .build());
+
+ private Long value = 0L;
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
+ executor.register(SET, SERIALIZER::decode, this::set);
+ executor.register(GET, this::get, SERIALIZER::encode);
+ executor.register(COMPARE_AND_SET, SERIALIZER::decode, this::compareAndSet, SERIALIZER::encode);
+ executor.register(INCREMENT_AND_GET, this::incrementAndGet, SERIALIZER::encode);
+ executor.register(GET_AND_INCREMENT, this::getAndIncrement, SERIALIZER::encode);
+ executor.register(ADD_AND_GET, SERIALIZER::decode, this::addAndGet, SERIALIZER::encode);
+ executor.register(GET_AND_ADD, SERIALIZER::decode, this::getAndAdd, SERIALIZER::encode);
+ }
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeLong(value);
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ value = reader.readLong();
+ }
+
+ /**
+ * Handles a set commit.
+ *
+ * @param commit the commit to handle
+ */
+ protected void set(Commit<Set> commit) {
+ value = commit.value().value();
+ }
+
+ /**
+ * Handles a get commit.
+ *
+ * @param commit the commit to handle
+ * @return counter value
+ */
+ protected Long get(Commit<Void> commit) {
+ return value;
+ }
+
+ /**
+ * Handles a compare and set commit.
+ *
+ * @param commit the commit to handle
+ * @return counter value
+ */
+ protected boolean compareAndSet(Commit<CompareAndSet> commit) {
+ if (Objects.equals(value, commit.value().expect())) {
+ value = commit.value().update();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handles an increment and get commit.
+ *
+ * @param commit the commit to handle
+ * @return counter value
+ */
+ protected long incrementAndGet(Commit<Void> commit) {
+ Long oldValue = value;
+ value = oldValue + 1;
+ return value;
+ }
+
+ /**
+ * Handles a get and increment commit.
+ *
+ * @param commit the commit to handle
+ * @return counter value
+ */
+ protected long getAndIncrement(Commit<Void> commit) {
+ Long oldValue = value;
+ value = oldValue + 1;
+ return oldValue;
+ }
+
+ /**
+ * Handles an add and get commit.
+ *
+ * @param commit the commit to handle
+ * @return counter value
+ */
+ protected long addAndGet(Commit<AddAndGet> commit) {
+ Long oldValue = value;
+ value = oldValue + commit.value().delta();
+ return value;
+ }
+
+ /**
+ * Handles a get and add commit.
+ *
+ * @param commit the commit to handle
+ * @return counter value
+ */
+ protected long getAndAdd(Commit<GetAndAdd> commit) {
+ Long oldValue = value;
+ value = oldValue + commit.value().delta();
+ return oldValue;
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java
index 3875e55..1730fe8 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java
@@ -16,70 +16,65 @@
package org.onosproject.store.primitives.resources.impl;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.ILLEGAL_MODIFICATION;
-import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.INVALID_PATH;
-import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.OK;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import com.google.common.util.concurrent.MoreExecutors;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
import org.onlab.util.Match;
import org.onlab.util.Tools;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Get;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.GetChildren;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Listen;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Unlisten;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Update;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Get;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GetChildren;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Listen;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Unlisten;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Update;
+import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.AsyncDocumentTree;
import org.onosproject.store.service.DocumentPath;
import org.onosproject.store.service.DocumentTreeEvent;
import org.onosproject.store.service.DocumentTreeListener;
import org.onosproject.store.service.IllegalDocumentModificationException;
import org.onosproject.store.service.NoSuchDocumentPathException;
+import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.Versioned;
-import com.google.common.util.concurrent.MoreExecutors;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET_CHILDREN;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.UPDATE;
+import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.ILLEGAL_MODIFICATION;
+import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.INVALID_PATH;
+import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.OK;
/**
* Distributed resource providing the {@link AsyncDocumentTree} primitive.
*/
-@ResourceTypeInfo(id = -156, factory = AtomixDocumentTreeFactory.class)
-public class AtomixDocumentTree extends AbstractResource<AtomixDocumentTree>
- implements AsyncDocumentTree<byte[]> {
+public class AtomixDocumentTree extends AbstractRaftPrimitive implements AsyncDocumentTree<byte[]> {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixDocumentTreeOperations.NAMESPACE)
+ .register(AtomixDocumentTreeEvents.NAMESPACE)
+ .build());
private final Map<DocumentTreeListener<byte[]>, InternalListener> eventListeners = new HashMap<>();
- public static final String CHANGE_SUBJECT = "changeEvents";
- protected AtomixDocumentTree(CopycatClient client, Properties options) {
- super(client, options);
- }
-
- @Override
- public CompletableFuture<AtomixDocumentTree> open() {
- return super.open().thenApply(result -> {
- client.onStateChange(state -> {
- if (state == CopycatClient.State.CONNECTED && isListening()) {
- client.submit(new Listen());
- }
- });
- client.onEvent(CHANGE_SUBJECT, this::processTreeUpdates);
- return result;
+ public AtomixDocumentTree(RaftProxy proxy) {
+ super(proxy);
+ proxy.addStateChangeListener(state -> {
+ if (state == RaftProxy.State.CONNECTED && isListening()) {
+ proxy.invoke(ADD_LISTENER, SERIALIZER::encode, new Listen());
+ }
});
- }
-
- @Override
- public String name() {
- return null;
+ proxy.addEventListener(CHANGE, SERIALIZER::decode, this::processTreeUpdates);
}
@Override
@@ -89,7 +84,7 @@
@Override
public CompletableFuture<Void> destroy() {
- return client.submit(new Clear());
+ return proxy.invoke(CLEAR);
}
@Override
@@ -99,17 +94,20 @@
@Override
public CompletableFuture<Map<String, Versioned<byte[]>>> getChildren(DocumentPath path) {
- return client.submit(new GetChildren(checkNotNull(path)));
+ return proxy.invoke(GET_CHILDREN, SERIALIZER::encode, new GetChildren(checkNotNull(path)), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<byte[]>> get(DocumentPath path) {
- return client.submit(new Get(checkNotNull(path)));
+ return proxy.invoke(GET, SERIALIZER::encode, new Get(checkNotNull(path)), SERIALIZER::decode);
}
@Override
public CompletableFuture<Versioned<byte[]>> set(DocumentPath path, byte[] value) {
- return client.submit(new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.any()))
+ return proxy.<Update, DocumentTreeUpdateResult<byte[]>>invoke(UPDATE,
+ SERIALIZER::encode,
+ new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.any()),
+ SERIALIZER::decode)
.thenCompose(result -> {
if (result.status() == INVALID_PATH) {
return Tools.exceptionalFuture(new NoSuchDocumentPathException());
@@ -138,7 +136,7 @@
.thenCompose(status -> {
if (status == ILLEGAL_MODIFICATION) {
return createRecursive(path.parent(), null)
- .thenCompose(r -> createInternal(path, value).thenApply(v -> true));
+ .thenCompose(r -> createInternal(path, value).thenApply(v -> true));
}
return CompletableFuture.completedFuture(status == OK);
});
@@ -146,19 +144,24 @@
@Override
public CompletableFuture<Boolean> replace(DocumentPath path, byte[] newValue, long version) {
- return client.submit(new Update(checkNotNull(path),
- Optional.ofNullable(newValue),
- Match.any(),
- Match.ifValue(version)))
+ return proxy.<Update, DocumentTreeUpdateResult<byte[]>>invoke(UPDATE,
+ SERIALIZER::encode,
+ new Update(checkNotNull(path),
+ Optional.ofNullable(newValue),
+ Match.any(),
+ Match.ifValue(version)), SERIALIZER::decode)
.thenApply(result -> result.updated());
}
@Override
public CompletableFuture<Boolean> replace(DocumentPath path, byte[] newValue, byte[] currentValue) {
- return client.submit(new Update(checkNotNull(path),
- Optional.ofNullable(newValue),
- Match.ifValue(currentValue),
- Match.any()))
+ return proxy.<Update, DocumentTreeUpdateResult<byte[]>>invoke(UPDATE,
+ SERIALIZER::encode,
+ new Update(checkNotNull(path),
+ Optional.ofNullable(newValue),
+ Match.ifValue(currentValue),
+ Match.any()),
+ SERIALIZER::decode)
.thenCompose(result -> {
if (result.status() == INVALID_PATH) {
return Tools.exceptionalFuture(new NoSuchDocumentPathException());
@@ -175,7 +178,10 @@
if (path.equals(DocumentPath.from("root"))) {
return Tools.exceptionalFuture(new IllegalDocumentModificationException());
}
- return client.submit(new Update(checkNotNull(path), null, Match.any(), Match.ifNotNull()))
+ return proxy.<Update, DocumentTreeUpdateResult<byte[]>>invoke(UPDATE,
+ SERIALIZER::encode,
+ new Update(checkNotNull(path), null, Match.any(), Match.ifNotNull()),
+ SERIALIZER::decode)
.thenCompose(result -> {
if (result.status() == INVALID_PATH) {
return Tools.exceptionalFuture(new NoSuchDocumentPathException());
@@ -194,8 +200,8 @@
InternalListener internalListener = new InternalListener(path, listener, MoreExecutors.directExecutor());
// TODO: Support API that takes an executor
if (!eventListeners.containsKey(listener)) {
- return client.submit(new Listen(path))
- .thenRun(() -> eventListeners.put(listener, internalListener));
+ return proxy.invoke(ADD_LISTENER, SERIALIZER::encode, new Listen(path))
+ .thenRun(() -> eventListeners.put(listener, internalListener));
}
return CompletableFuture.completedFuture(null);
}
@@ -205,14 +211,18 @@
checkNotNull(listener);
InternalListener internalListener = eventListeners.remove(listener);
if (internalListener != null && eventListeners.isEmpty()) {
- return client.submit(new Unlisten(internalListener.path)).thenApply(v -> null);
+ return proxy.invoke(REMOVE_LISTENER, SERIALIZER::encode, new Unlisten(internalListener.path))
+ .thenApply(v -> null);
}
return CompletableFuture.completedFuture(null);
}
private CompletableFuture<DocumentTreeUpdateResult.Status> createInternal(DocumentPath path, byte[] value) {
- return client.submit(new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.ifNull()))
- .thenApply(result -> result.status());
+ return proxy.<Update, DocumentTreeUpdateResult<byte[]>>invoke(UPDATE,
+ SERIALIZER::encode,
+ new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.ifNull()),
+ SERIALIZER::decode)
+ .thenApply(result -> result.status());
}
private boolean isListening() {
@@ -242,4 +252,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeCommands.java
deleted file mode 100644
index 153dbcb..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeCommands.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-
-import java.util.Map;
-import java.util.Optional;
-
-import org.onlab.util.Match;
-import org.onosproject.store.service.DocumentPath;
-import org.onosproject.store.service.Versioned;
-
-import com.google.common.base.MoreObjects;
-
-/**
- * {@link AtomixDocumentTree} resource state machine operations.
- */
-public class AtomixDocumentTreeCommands {
-
- /**
- * Abstract DocumentTree operation.
- */
- public abstract static class DocumentTreeOperation<V> implements CatalystSerializable {
-
- private DocumentPath path;
-
- DocumentTreeOperation(DocumentPath path) {
- this.path = path;
- }
-
- public DocumentPath path() {
- return path;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- serializer.writeObject(path, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- path = serializer.readObject(buffer);
- }
- }
-
- /**
- * Abstract DocumentTree query.
- */
- @SuppressWarnings("serial")
- public abstract static class DocumentTreeQuery<V> extends DocumentTreeOperation<V> implements Query<V> {
-
- DocumentTreeQuery(DocumentPath path) {
- super(path);
- }
-
- @Override
- public ConsistencyLevel consistency() {
- return ConsistencyLevel.SEQUENTIAL;
- }
- }
-
- /**
- * Abstract DocumentTree command.
- */
- @SuppressWarnings("serial")
- public abstract static class DocumentTreeCommand<V> extends DocumentTreeOperation<V> implements Command<V> {
-
- DocumentTreeCommand(DocumentPath path) {
- super(path);
- }
- }
-
- /**
- * DocumentTree#get query.
- */
- @SuppressWarnings("serial")
- public static class Get extends DocumentTreeQuery<Versioned<byte[]>> {
- public Get() {
- super(null);
- }
-
- public Get(DocumentPath path) {
- super(path);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("path", path())
- .toString();
- }
- }
-
- /**
- * DocumentTree#getChildren query.
- */
- @SuppressWarnings("serial")
- public static class GetChildren extends DocumentTreeQuery<Map<String, Versioned<byte[]>>> {
- public GetChildren() {
- super(null);
- }
-
- public GetChildren(DocumentPath path) {
- super(path);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("path", path())
- .toString();
- }
- }
-
- /**
- * DocumentTree update command.
- */
- @SuppressWarnings("serial")
- public static class Update extends DocumentTreeCommand<DocumentTreeUpdateResult<byte[]>> {
-
- private Optional<byte[]> value;
- private Match<byte[]> valueMatch;
- private Match<Long> versionMatch;
-
- public Update() {
- super(null);
- this.value = null;
- this.valueMatch = null;
- this.versionMatch = null;
- }
-
- public Update(DocumentPath path, Optional<byte[]> value, Match<byte[]> valueMatch, Match<Long> versionMatch) {
- super(path);
- this.value = value;
- this.valueMatch = valueMatch;
- this.versionMatch = versionMatch;
- }
-
- public Optional<byte[]> value() {
- return value;
- }
-
- public Match<byte[]> valueMatch() {
- return valueMatch;
- }
-
- public Match<Long> versionMatch() {
- return versionMatch;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(value, buffer);
- serializer.writeObject(valueMatch, buffer);
- serializer.writeObject(versionMatch, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- value = serializer.readObject(buffer);
- valueMatch = serializer.readObject(buffer);
- versionMatch = serializer.readObject(buffer);
- }
-
- @Override
- public CompactionMode compaction() {
- return value == null ? CompactionMode.TOMBSTONE : CompactionMode.QUORUM;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("path", path())
- .add("value", value)
- .add("valueMatch", valueMatch)
- .add("versionMatch", versionMatch)
- .toString();
- }
- }
-
- /**
- * Clear command.
- */
- @SuppressWarnings("serial")
- public static class Clear implements Command<Void>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * Change listen.
- */
- @SuppressWarnings("serial")
- public static class Listen extends DocumentTreeCommand<Void> {
-
- public Listen() {
- this(DocumentPath.from("root"));
- }
-
- public Listen(DocumentPath path) {
- super(path);
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("path", path())
- .toString();
- }
- }
-
- /**
- * Change unlisten.
- */
- @SuppressWarnings("serial")
- public static class Unlisten extends DocumentTreeCommand<Void> {
-
- public Unlisten() {
- this(DocumentPath.from("root"));
- }
-
- public Unlisten(DocumentPath path) {
- super(path);
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("path", path())
- .toString();
- }
- }
-
- /**
- * DocumentTree command type resolver.
- */
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- registry.register(Get.class, -911);
- registry.register(GetChildren.class, -912);
- registry.register(Update.class, -913);
- registry.register(Listen.class, -914);
- registry.register(Unlisten.class, -915);
- registry.register(Clear.class, -916);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java
new file mode 100644
index 0000000..8aa23ce
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.event.EventType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.DocumentTreeEvent;
+
+/**
+ * Atomix document tree events.
+ */
+public enum AtomixDocumentTreeEvents implements EventType {
+ CHANGE("change");
+
+ private final String id;
+
+ AtomixDocumentTreeEvents(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50)
+ .register(DocumentTreeEvent.class)
+ .register(DocumentTreeEvent.Type.class)
+ .build("AtomixDocumentTreeEvents");
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java
deleted file mode 100644
index 4282566..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * {@link AtomixDocumentTree} resource factory.
- *
- */
-public class AtomixDocumentTreeFactory implements ResourceFactory<AtomixDocumentTree> {
-
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixDocumentTreeCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixDocumentTreeState(config);
- }
-
- @Override
- public AtomixDocumentTree createInstance(CopycatClient client, Properties options) {
- return new AtomixDocumentTree(client, options);
- }
- }
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeOperations.java
new file mode 100644
index 0000000..f4213e2
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeOperations.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Optional;
+
+import com.google.common.base.MoreObjects;
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.DocumentPath;
+import org.onosproject.store.service.Versioned;
+
+/**
+ * {@link AtomixDocumentTree} resource state machine operations.
+ */
+public enum AtomixDocumentTreeOperations implements OperationId {
+ ADD_LISTENER("set", OperationType.COMMAND),
+ REMOVE_LISTENER("compareAndSet", OperationType.COMMAND),
+ GET("incrementAndGet", OperationType.QUERY),
+ GET_CHILDREN("getAndIncrement", OperationType.QUERY),
+ UPDATE("addAndGet", OperationType.COMMAND),
+ CLEAR("getAndAdd", OperationType.COMMAND);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixDocumentTreeOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(Listen.class)
+ .register(Unlisten.class)
+ .register(Get.class)
+ .register(GetChildren.class)
+ .register(Update.class)
+ .register(DocumentPath.class)
+ .register(Match.class)
+ .register(Versioned.class)
+ .register(DocumentTreeUpdateResult.class)
+ .register(DocumentTreeUpdateResult.Status.class)
+ .build("AtomixDocumentTreeOperations");
+
+ /**
+ * Abstract DocumentTree command.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class DocumentTreeOperation {
+ private DocumentPath path;
+
+ DocumentTreeOperation(DocumentPath path) {
+ this.path = path;
+ }
+
+ public DocumentPath path() {
+ return path;
+ }
+ }
+
+ /**
+ * DocumentTree#get query.
+ */
+ @SuppressWarnings("serial")
+ public static class Get extends DocumentTreeOperation {
+ public Get() {
+ super(null);
+ }
+
+ public Get(DocumentPath path) {
+ super(path);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("path", path())
+ .toString();
+ }
+ }
+
+ /**
+ * DocumentTree#getChildren query.
+ */
+ @SuppressWarnings("serial")
+ public static class GetChildren extends DocumentTreeOperation {
+ public GetChildren() {
+ super(null);
+ }
+
+ public GetChildren(DocumentPath path) {
+ super(path);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("path", path())
+ .toString();
+ }
+ }
+
+ /**
+ * DocumentTree update command.
+ */
+ @SuppressWarnings("serial")
+ public static class Update extends DocumentTreeOperation {
+ private Optional<byte[]> value;
+ private Match<byte[]> valueMatch;
+ private Match<Long> versionMatch;
+
+ public Update() {
+ super(null);
+ this.value = null;
+ this.valueMatch = null;
+ this.versionMatch = null;
+ }
+
+ public Update(DocumentPath path, Optional<byte[]> value, Match<byte[]> valueMatch, Match<Long> versionMatch) {
+ super(path);
+ this.value = value;
+ this.valueMatch = valueMatch;
+ this.versionMatch = versionMatch;
+ }
+
+ public Optional<byte[]> value() {
+ return value;
+ }
+
+ public Match<byte[]> valueMatch() {
+ return valueMatch;
+ }
+
+ public Match<Long> versionMatch() {
+ return versionMatch;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("path", path())
+ .add("value", value)
+ .add("valueMatch", valueMatch)
+ .add("versionMatch", versionMatch)
+ .toString();
+ }
+ }
+
+ /**
+ * Change listen.
+ */
+ @SuppressWarnings("serial")
+ public static class Listen extends DocumentTreeOperation {
+ public Listen() {
+ this(DocumentPath.from("root"));
+ }
+
+ public Listen(DocumentPath path) {
+ super(path);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("path", path())
+ .toString();
+ }
+ }
+
+ /**
+ * Change unlisten.
+ */
+ @SuppressWarnings("serial")
+ public static class Unlisten extends DocumentTreeOperation {
+ public Unlisten() {
+ this(DocumentPath.from("root"));
+ }
+
+ public Unlisten(DocumentPath path) {
+ super(path);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("path", path())
+ .toString();
+ }
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java
new file mode 100644
index 0000000..6b7c550
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Queues;
+import io.atomix.protocols.raft.event.EventType;
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.session.RaftSession;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Match;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Get;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GetChildren;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Listen;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Unlisten;
+import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Update;
+import org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.DocumentPath;
+import org.onosproject.store.service.DocumentTree;
+import org.onosproject.store.service.DocumentTreeEvent;
+import org.onosproject.store.service.DocumentTreeEvent.Type;
+import org.onosproject.store.service.IllegalDocumentModificationException;
+import org.onosproject.store.service.NoSuchDocumentPathException;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Versioned;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET_CHILDREN;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.UPDATE;
+
+/**
+ * State Machine for {@link AtomixDocumentTree} resource.
+ */
+public class AtomixDocumentTreeService extends AbstractRaftService {
+ private final Serializer serializer = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixDocumentTreeOperations.NAMESPACE)
+ .register(AtomixDocumentTreeEvents.NAMESPACE)
+ .register(new com.esotericsoftware.kryo.Serializer<Listener>() {
+ @Override
+ public void write(Kryo kryo, Output output, Listener listener) {
+ output.writeLong(listener.session.sessionId().id());
+ kryo.writeObject(output, listener.path);
+ }
+
+ @Override
+ public Listener read(Kryo kryo, Input input, Class<Listener> type) {
+ return new Listener(getSessions().getSession(input.readLong()),
+ kryo.readObjectOrNull(input, DocumentPath.class));
+ }
+ }, Listener.class)
+ .register(Versioned.class)
+ .register(DocumentPath.class)
+ .register(new HashMap().keySet().getClass())
+ .register(TreeMap.class)
+ .register(SessionListenCommits.class)
+ .register(new com.esotericsoftware.kryo.Serializer<DefaultDocumentTree>() {
+ @Override
+ public void write(Kryo kryo, Output output, DefaultDocumentTree object) {
+ kryo.writeObject(output, object.root);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public DefaultDocumentTree read(Kryo kryo, Input input, Class<DefaultDocumentTree> type) {
+ return new DefaultDocumentTree(versionCounter::incrementAndGet,
+ kryo.readObject(input, DefaultDocumentTreeNode.class));
+ }
+ }, DefaultDocumentTree.class)
+ .register(DefaultDocumentTreeNode.class)
+ .build());
+
+ private Map<Long, SessionListenCommits> listeners = new HashMap<>();
+ private AtomicLong versionCounter = new AtomicLong(0);
+ private DocumentTree<byte[]> docTree = new DefaultDocumentTree<>(versionCounter::incrementAndGet);
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeLong(versionCounter.get());
+ writer.writeObject(listeners, serializer::encode);
+ writer.writeObject(docTree, serializer::encode);
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ versionCounter = new AtomicLong(reader.readLong());
+ listeners = reader.readObject(serializer::decode);
+ docTree = reader.readObject(serializer::decode);
+ }
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
+ // Listeners
+ executor.register(ADD_LISTENER, serializer::decode, this::listen);
+ executor.register(REMOVE_LISTENER, serializer::decode, this::unlisten);
+ // queries
+ executor.register(GET, serializer::decode, this::get, serializer::encode);
+ executor.register(GET_CHILDREN, serializer::decode, this::getChildren, serializer::encode);
+ // commands
+ executor.register(UPDATE, serializer::decode, this::update, serializer::encode);
+ executor.register(CLEAR, this::clear);
+ }
+
+ protected void listen(Commit<? extends Listen> commit) {
+ Long sessionId = commit.session().sessionId().id();
+ listeners.computeIfAbsent(sessionId, k -> new SessionListenCommits())
+ .add(new Listener(commit.session(), commit.value().path()));
+ }
+
+ protected void unlisten(Commit<? extends Unlisten> commit) {
+ Long sessionId = commit.session().sessionId().id();
+ SessionListenCommits listenCommits = listeners.get(sessionId);
+ if (listenCommits != null) {
+ listenCommits.remove(commit);
+ }
+ }
+
+ protected Versioned<byte[]> get(Commit<? extends Get> commit) {
+ try {
+ Versioned<byte[]> value = docTree.get(commit.value().path());
+ return value == null ? null : value.map(node -> node == null ? null : node);
+ } catch (IllegalStateException e) {
+ return null;
+ }
+ }
+
+ protected Map<String, Versioned<byte[]>> getChildren(Commit<? extends GetChildren> commit) {
+ return docTree.getChildren(commit.value().path());
+ }
+
+ protected DocumentTreeUpdateResult<byte[]> update(Commit<? extends Update> commit) {
+ DocumentTreeUpdateResult<byte[]> result = null;
+ DocumentPath path = commit.value().path();
+ boolean updated = false;
+ Versioned<byte[]> currentValue = docTree.get(path);
+ try {
+ Match<Long> versionMatch = commit.value().versionMatch();
+ Match<byte[]> valueMatch = commit.value().valueMatch();
+
+ if (versionMatch.matches(currentValue == null ? null : currentValue.version())
+ && valueMatch.matches(currentValue == null ? null : currentValue.value())) {
+ if (commit.value().value() == null) {
+ docTree.removeNode(path);
+ } else {
+ docTree.set(path, commit.value().value().orElse(null));
+ }
+ updated = true;
+ }
+ Versioned<byte[]> newValue = updated ? docTree.get(path) : currentValue;
+ Status updateStatus = updated
+ ? Status.OK : commit.value().value() == null ? Status.INVALID_PATH : Status.NOOP;
+ result = new DocumentTreeUpdateResult<>(path, updateStatus, newValue, currentValue);
+ } catch (IllegalDocumentModificationException e) {
+ result = DocumentTreeUpdateResult.illegalModification(path);
+ } catch (NoSuchDocumentPathException e) {
+ result = DocumentTreeUpdateResult.invalidPath(path);
+ } catch (Exception e) {
+ getLogger().error("Failed to apply {} to state machine", commit.value(), e);
+ throw Throwables.propagate(e);
+ }
+ notifyListeners(path, result);
+ return result;
+ }
+
+ protected void clear(Commit<Void> commit) {
+ Queue<DocumentPath> toClearQueue = Queues.newArrayDeque();
+ Map<String, Versioned<byte[]>> topLevelChildren = docTree.getChildren(DocumentPath.from("root"));
+ toClearQueue.addAll(topLevelChildren.keySet()
+ .stream()
+ .map(name -> new DocumentPath(name, DocumentPath.from("root")))
+ .collect(Collectors.toList()));
+ while (!toClearQueue.isEmpty()) {
+ DocumentPath path = toClearQueue.remove();
+ Map<String, Versioned<byte[]>> children = docTree.getChildren(path);
+ if (children.size() == 0) {
+ docTree.removeNode(path);
+ } else {
+ children.keySet().forEach(name -> toClearQueue.add(new DocumentPath(name, path)));
+ toClearQueue.add(path);
+ }
+ }
+ }
+
+ private void notifyListeners(DocumentPath path, DocumentTreeUpdateResult<byte[]> result) {
+ if (result.status() != Status.OK) {
+ return;
+ }
+ DocumentTreeEvent<byte[]> event =
+ new DocumentTreeEvent<>(path,
+ result.created() ? Type.CREATED : result.newValue() == null ? Type.DELETED : Type.UPDATED,
+ Optional.ofNullable(result.newValue()),
+ Optional.ofNullable(result.oldValue()));
+
+ listeners.values()
+ .stream()
+ .filter(l -> event.path().isDescendentOf(l.leastCommonAncestorPath()))
+ .forEach(listener -> listener.publish(CHANGE, Arrays.asList(event)));
+ }
+
+ @Override
+ public void onExpire(RaftSession session) {
+ closeListener(session.sessionId().id());
+ }
+
+ @Override
+ public void onClose(RaftSession session) {
+ closeListener(session.sessionId().id());
+ }
+
+ private void closeListener(Long sessionId) {
+ listeners.remove(sessionId);
+ }
+
+ private class SessionListenCommits {
+ private final List<Listener> listeners = Lists.newArrayList();
+ private DocumentPath leastCommonAncestorPath;
+
+ public void add(Listener listener) {
+ listeners.add(listener);
+ recomputeLeastCommonAncestor();
+ }
+
+ public void remove(Commit<? extends Unlisten> commit) {
+ // Remove the first listen commit with path matching path in unlisten commit
+ Iterator<Listener> iterator = listeners.iterator();
+ while (iterator.hasNext()) {
+ Listener listener = iterator.next();
+ if (listener.path().equals(commit.value().path())) {
+ iterator.remove();
+ }
+ }
+ recomputeLeastCommonAncestor();
+ }
+
+ public DocumentPath leastCommonAncestorPath() {
+ return leastCommonAncestorPath;
+ }
+
+ public <M> void publish(EventType topic, M message) {
+ listeners.stream().findAny().ifPresent(listener ->
+ listener.session().publish(topic, serializer::encode, message));
+ }
+
+ private void recomputeLeastCommonAncestor() {
+ this.leastCommonAncestorPath = DocumentPath.leastCommonAncestor(listeners.stream()
+ .map(Listener::path)
+ .collect(Collectors.toList()));
+ }
+ }
+
+ private static class Listener {
+ private final RaftSession session;
+ private final DocumentPath path;
+
+ public Listener(RaftSession session, DocumentPath path) {
+ this.session = session;
+ this.path = path;
+ }
+
+ public DocumentPath path() {
+ return path;
+ }
+
+ public RaftSession session() {
+ return session;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java
deleted file mode 100644
index 8a8a23c..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.store.primitives.resources.impl;
-
-import static org.slf4j.LoggerFactory.getLogger;
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.Snapshottable;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.session.ServerSession;
-import io.atomix.copycat.server.session.SessionListener;
-import io.atomix.copycat.server.storage.snapshot.SnapshotReader;
-import io.atomix.copycat.server.storage.snapshot.SnapshotWriter;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
-
-import org.onlab.util.Match;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Get;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.GetChildren;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Listen;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Unlisten;
-import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Update;
-import org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status;
-import org.onosproject.store.service.DocumentPath;
-import org.onosproject.store.service.DocumentTree;
-import org.onosproject.store.service.DocumentTreeEvent;
-import org.onosproject.store.service.DocumentTreeEvent.Type;
-import org.onosproject.store.service.IllegalDocumentModificationException;
-import org.onosproject.store.service.NoSuchDocumentPathException;
-import org.onosproject.store.service.Versioned;
-import org.slf4j.Logger;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Queues;
-
-/**
- * State Machine for {@link AtomixDocumentTree} resource.
- */
-public class AtomixDocumentTreeState
- extends ResourceStateMachine
- implements SessionListener, Snapshottable {
-
- private final Logger log = getLogger(getClass());
- private final Map<Long, SessionListenCommits> listeners = new HashMap<>();
- private AtomicLong versionCounter = new AtomicLong(0);
- private final DocumentTree<TreeNodeValue> docTree = new DefaultDocumentTree<>(versionCounter::incrementAndGet);
-
- public AtomixDocumentTreeState(Properties properties) {
- super(properties);
- }
-
- @Override
- public void snapshot(SnapshotWriter writer) {
- writer.writeLong(versionCounter.get());
- }
-
- @Override
- public void install(SnapshotReader reader) {
- versionCounter = new AtomicLong(reader.readLong());
- }
-
- @Override
- protected void configure(StateMachineExecutor executor) {
- // Listeners
- executor.register(Listen.class, this::listen);
- executor.register(Unlisten.class, this::unlisten);
- // queries
- executor.register(Get.class, this::get);
- executor.register(GetChildren.class, this::getChildren);
- // commands
- executor.register(Update.class, this::update);
- executor.register(Clear.class, this::clear);
- }
-
- protected void listen(Commit<? extends Listen> commit) {
- Long sessionId = commit.session().id();
- listeners.computeIfAbsent(sessionId, k -> new SessionListenCommits()).add(commit);
- commit.session().onStateChange(
- state -> {
- if (state == ServerSession.State.CLOSED
- || state == ServerSession.State.EXPIRED) {
- closeListener(commit.session().id());
- }
- });
- }
-
- protected void unlisten(Commit<? extends Unlisten> commit) {
- Long sessionId = commit.session().id();
- try {
- SessionListenCommits listenCommits = listeners.get(sessionId);
- if (listenCommits != null) {
- listenCommits.remove(commit);
- }
- } finally {
- commit.close();
- }
- }
-
- protected Versioned<byte[]> get(Commit<? extends Get> commit) {
- try {
- Versioned<TreeNodeValue> value = docTree.get(commit.operation().path());
- return value == null ? null : value.map(node -> node == null ? null : node.value());
- } catch (IllegalStateException e) {
- return null;
- } finally {
- commit.close();
- }
- }
-
- protected Map<String, Versioned<byte[]>> getChildren(Commit<? extends GetChildren> commit) {
- try {
- Map<String, Versioned<TreeNodeValue>> children = docTree.getChildren(commit.operation().path());
- return children == null
- ? null : Maps.newHashMap(Maps.transformValues(children,
- value -> value.map(TreeNodeValue::value)));
- } finally {
- commit.close();
- }
- }
-
- protected DocumentTreeUpdateResult<byte[]> update(Commit<? extends Update> commit) {
- DocumentTreeUpdateResult<byte[]> result = null;
- DocumentPath path = commit.operation().path();
- boolean updated = false;
- Versioned<TreeNodeValue> currentValue = docTree.get(path);
- try {
- Match<Long> versionMatch = commit.operation().versionMatch();
- Match<byte[]> valueMatch = commit.operation().valueMatch();
-
- if (versionMatch.matches(currentValue == null ? null : currentValue.version())
- && valueMatch.matches(currentValue == null ? null : currentValue.value().value())) {
- if (commit.operation().value() == null) {
- docTree.removeNode(path);
- } else {
- docTree.set(path, new NonTransactionalCommit(commit));
- }
- updated = true;
- }
- Versioned<TreeNodeValue> newValue = updated ? docTree.get(path) : currentValue;
- Status updateStatus = updated
- ? Status.OK : commit.operation().value() == null ? Status.INVALID_PATH : Status.NOOP;
- result = new DocumentTreeUpdateResult<>(path,
- updateStatus,
- newValue == null
- ? null : newValue.map(TreeNodeValue::value),
- currentValue == null
- ? null : currentValue.map(TreeNodeValue::value));
- } catch (IllegalDocumentModificationException e) {
- result = DocumentTreeUpdateResult.illegalModification(path);
- } catch (NoSuchDocumentPathException e) {
- result = DocumentTreeUpdateResult.invalidPath(path);
- } catch (Exception e) {
- log.error("Failed to apply {} to state machine", commit.operation(), e);
- throw Throwables.propagate(e);
- } finally {
- if (updated) {
- if (currentValue != null) {
- currentValue.value().discard();
- }
- } else {
- commit.close();
- }
- }
- notifyListeners(path, result);
- return result;
- }
-
- protected void clear(Commit<? extends Clear> commit) {
- try {
- Queue<DocumentPath> toClearQueue = Queues.newArrayDeque();
- Map<String, Versioned<TreeNodeValue>> topLevelChildren = docTree.getChildren(DocumentPath.from("root"));
- toClearQueue.addAll(topLevelChildren.keySet()
- .stream()
- .map(name -> new DocumentPath(name, DocumentPath.from("root")))
- .collect(Collectors.toList()));
- while (!toClearQueue.isEmpty()) {
- DocumentPath path = toClearQueue.remove();
- Map<String, Versioned<TreeNodeValue>> children = docTree.getChildren(path);
- if (children.size() == 0) {
- docTree.removeNode(path).value().discard();
- } else {
- children.keySet()
- .stream()
- .forEach(name -> toClearQueue.add(new DocumentPath(name, path)));
- toClearQueue.add(path);
- }
- }
- } finally {
- commit.close();
- }
- }
-
- /**
- * Interface implemented by tree node values.
- */
- private interface TreeNodeValue {
- /**
- * Returns the raw {@code byte[]}.
- *
- * @return raw value
- */
- byte[] value();
-
- /**
- * Discards the value by invoke appropriate clean up actions.
- */
- void discard();
- }
-
- /**
- * A {@code TreeNodeValue} that is derived from a non-transactional update
- * i.e. via any standard tree update operation.
- */
- private class NonTransactionalCommit implements TreeNodeValue {
- private final Commit<? extends Update> commit;
-
- public NonTransactionalCommit(Commit<? extends Update> commit) {
- this.commit = commit;
- }
-
- @Override
- public byte[] value() {
- return commit.operation().value().orElse(null);
- }
-
- @Override
- public void discard() {
- commit.close();
- }
- }
-
- private void notifyListeners(DocumentPath path, DocumentTreeUpdateResult<byte[]> result) {
- if (result.status() != Status.OK) {
- return;
- }
- DocumentTreeEvent<byte[]> event =
- new DocumentTreeEvent<>(path,
- result.created() ? Type.CREATED : result.newValue() == null ? Type.DELETED : Type.UPDATED,
- Optional.ofNullable(result.newValue()),
- Optional.ofNullable(result.oldValue()));
-
- listeners.values()
- .stream()
- .filter(l -> event.path().isDescendentOf(l.leastCommonAncestorPath()))
- .forEach(listener -> listener.publish(AtomixDocumentTree.CHANGE_SUBJECT, Arrays.asList(event)));
- }
-
- @Override
- public void register(ServerSession session) {
- }
-
- @Override
- public void unregister(ServerSession session) {
- closeListener(session.id());
- }
-
- @Override
- public void expire(ServerSession session) {
- closeListener(session.id());
- }
-
- @Override
- public void close(ServerSession session) {
- closeListener(session.id());
- }
-
- private void closeListener(Long sessionId) {
- SessionListenCommits listenCommits = listeners.remove(sessionId);
- if (listenCommits != null) {
- listenCommits.close();
- }
- }
-
- private class SessionListenCommits {
- private final List<Commit<? extends Listen>> commits = Lists.newArrayList();
- private DocumentPath leastCommonAncestorPath;
-
- public void add(Commit<? extends Listen> commit) {
- commits.add(commit);
- recomputeLeastCommonAncestor();
- }
-
- public void remove(Commit<? extends Unlisten> commit) {
- // Remove the first listen commit with path matching path in unlisten commit
- Iterator<Commit<? extends Listen>> iterator = commits.iterator();
- while (iterator.hasNext()) {
- Commit<? extends Listen> listenCommit = iterator.next();
- if (listenCommit.operation().path().equals(commit.operation().path())) {
- iterator.remove();
- listenCommit.close();
- }
- }
- recomputeLeastCommonAncestor();
- }
-
- public DocumentPath leastCommonAncestorPath() {
- return leastCommonAncestorPath;
- }
-
- public <M> void publish(String topic, M message) {
- commits.stream().findAny().ifPresent(commit -> commit.session().publish(topic, message));
- }
-
- public void close() {
- commits.forEach(Commit::close);
- commits.clear();
- leastCommonAncestorPath = null;
- }
-
- private void recomputeLeastCommonAncestor() {
- this.leastCommonAncestorPath = DocumentPath.leastCommonAncestor(commits.stream()
- .map(c -> c.operation().path())
- .collect(Collectors.toList()));
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java
index 971b60c..7d487f4 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java
@@ -18,36 +18,34 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
-import io.atomix.variables.DistributedLong;
+import org.onosproject.store.service.AsyncAtomicCounter;
import org.onosproject.store.service.AsyncAtomicIdGenerator;
/**
* {@code AsyncAtomicIdGenerator} implementation backed by Atomix
- * {@link DistributedLong}.
+ * {@link AsyncAtomicCounter}.
*/
public class AtomixIdGenerator implements AsyncAtomicIdGenerator {
private static final long DEFAULT_BATCH_SIZE = 1000;
- private final String name;
- private final DistributedLong distLong;
+ private final AsyncAtomicCounter counter;
private final long batchSize;
private CompletableFuture<Long> reserveFuture;
private long base;
private final AtomicLong delta = new AtomicLong();
- public AtomixIdGenerator(String name, DistributedLong distLong) {
- this(name, distLong, DEFAULT_BATCH_SIZE);
+ public AtomixIdGenerator(AsyncAtomicCounter counter) {
+ this(counter, DEFAULT_BATCH_SIZE);
}
- AtomixIdGenerator(String name, DistributedLong distLong, long batchSize) {
- this.name = name;
- this.distLong = distLong;
+ AtomixIdGenerator(AsyncAtomicCounter counter, long batchSize) {
+ this.counter = counter;
this.batchSize = batchSize;
}
@Override
public String name() {
- return name;
+ return counter.name();
}
@Override
@@ -64,9 +62,9 @@
private CompletableFuture<Long> reserve() {
if (reserveFuture == null || reserveFuture.isDone()) {
- reserveFuture = distLong.getAndAdd(batchSize);
+ reserveFuture = counter.getAndAdd(batchSize);
} else {
- reserveFuture = reserveFuture.thenCompose(v -> distLong.getAndAdd(batchSize));
+ reserveFuture = reserveFuture.thenCompose(v -> counter.getAndAdd(batchSize));
}
reserveFuture = reserveFuture.thenApply(base -> {
this.base = base;
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java
index 3e45091..825fa98 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java
@@ -15,73 +15,65 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-
-import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
-import java.util.function.Function;
-import org.onosproject.cluster.Leadership;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.event.Change;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Anoint;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetAllLeaderships;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetElectedTopics;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetLeadership;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Listen;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Promote;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Run;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Unlisten;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Withdraw;
-import org.onosproject.store.service.AsyncLeaderElector;
-
-import com.google.common.collect.ImmutableSet;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.cluster.Leadership;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.event.Change;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Anoint;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetElectedTopics;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetLeadership;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Promote;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Run;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Withdraw;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.AsyncLeaderElector;
+import org.onosproject.store.service.Serializer;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ANOINT;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.EVICT;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ALL_LEADERSHIPS;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ELECTED_TOPICS;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_LEADERSHIP;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.PROMOTE;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.RUN;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.WITHDRAW;
/**
* Distributed resource providing the {@link AsyncLeaderElector} primitive.
*/
-@ResourceTypeInfo(id = -152, factory = AtomixLeaderElectorFactory.class)
-public class AtomixLeaderElector extends AbstractResource<AtomixLeaderElector>
- implements AsyncLeaderElector {
- private final Set<Consumer<Status>> statusChangeListeners =
- Sets.newCopyOnWriteArraySet();
- private final Set<Consumer<Change<Leadership>>> leadershipChangeListeners =
- Sets.newCopyOnWriteArraySet();
+public class AtomixLeaderElector extends AbstractRaftPrimitive implements AsyncLeaderElector {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(AtomixLeaderElectorOperations.NAMESPACE)
+ .register(AtomixLeaderElectorEvents.NAMESPACE)
+ .build());
+
+ private final Set<Consumer<Change<Leadership>>> leadershipChangeListeners = Sets.newCopyOnWriteArraySet();
private final Consumer<Change<Leadership>> cacheUpdater;
private final Consumer<Status> statusListener;
- public static final String CHANGE_SUBJECT = "leadershipChangeEvents";
private final LoadingCache<String, CompletableFuture<Leadership>> cache;
- Function<CopycatClient.State, Status> mapper = state -> {
- switch (state) {
- case CONNECTED:
- return Status.ACTIVE;
- case SUSPENDED:
- return Status.SUSPENDED;
- case CLOSED:
- return Status.INACTIVE;
- default:
- throw new IllegalStateException("Unknown state " + state);
- }
- };
-
- public AtomixLeaderElector(CopycatClient client, Properties properties) {
- super(client, properties);
+ public AtomixLeaderElector(RaftProxy proxy) {
+ super(proxy);
cache = CacheBuilder.newBuilder()
.maximumSize(1000)
- .build(CacheLoader.from(topic -> this.client.submit(new GetLeadership(topic))));
+ .build(CacheLoader.from(topic -> proxy.invoke(
+ GET_LEADERSHIP, SERIALIZER::encode, new GetLeadership(topic), SERIALIZER::decode)));
cacheUpdater = change -> {
Leadership leadership = change.newValue();
@@ -93,7 +85,13 @@
}
};
addStatusChangeListener(statusListener);
- client.onStateChange(this::handleStateChange);
+
+ proxy.addStateChangeListener(state -> {
+ if (state == RaftProxy.State.CONNECTED && isListening()) {
+ proxy.invoke(ADD_LISTENER);
+ }
+ });
+ proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent);
}
@Override
@@ -102,24 +100,6 @@
return removeChangeListener(cacheUpdater);
}
- @Override
- public String name() {
- return null;
- }
-
- @Override
- public CompletableFuture<AtomixLeaderElector> open() {
- return super.open().thenApply(result -> {
- client.onStateChange(state -> {
- if (state == CopycatClient.State.CONNECTED && isListening()) {
- client.submit(new Listen());
- }
- });
- client.onEvent(CHANGE_SUBJECT, this::handleEvent);
- return result;
- });
- }
-
public CompletableFuture<AtomixLeaderElector> setupCache() {
return addChangeListener(cacheUpdater).thenApply(v -> this);
}
@@ -130,27 +110,32 @@
@Override
public CompletableFuture<Leadership> run(String topic, NodeId nodeId) {
- return client.submit(new Run(topic, nodeId)).whenComplete((r, e) -> cache.invalidate(topic));
+ return proxy.<Run, Leadership>invoke(RUN, SERIALIZER::encode, new Run(topic, nodeId), SERIALIZER::decode)
+ .whenComplete((r, e) -> cache.invalidate(topic));
}
@Override
public CompletableFuture<Void> withdraw(String topic) {
- return client.submit(new Withdraw(topic)).whenComplete((r, e) -> cache.invalidate(topic));
+ return proxy.invoke(WITHDRAW, SERIALIZER::encode, new Withdraw(topic))
+ .whenComplete((r, e) -> cache.invalidate(topic));
}
@Override
public CompletableFuture<Boolean> anoint(String topic, NodeId nodeId) {
- return client.submit(new Anoint(topic, nodeId)).whenComplete((r, e) -> cache.invalidate(topic));
+ return proxy.<Anoint, Boolean>invoke(ANOINT, SERIALIZER::encode, new Anoint(topic, nodeId), SERIALIZER::decode)
+ .whenComplete((r, e) -> cache.invalidate(topic));
}
@Override
public CompletableFuture<Boolean> promote(String topic, NodeId nodeId) {
- return client.submit(new Promote(topic, nodeId)).whenComplete((r, e) -> cache.invalidate(topic));
+ return proxy.<Promote, Boolean>invoke(
+ PROMOTE, SERIALIZER::encode, new Promote(topic, nodeId), SERIALIZER::decode)
+ .whenComplete((r, e) -> cache.invalidate(topic));
}
@Override
public CompletableFuture<Void> evict(NodeId nodeId) {
- return client.submit(new AtomixLeaderElectorCommands.Evict(nodeId));
+ return proxy.invoke(EVICT, SERIALIZER::encode, new AtomixLeaderElectorOperations.Evict(nodeId));
}
@Override
@@ -165,17 +150,17 @@
@Override
public CompletableFuture<Map<String, Leadership>> getLeaderships() {
- return client.submit(new GetAllLeaderships());
+ return proxy.invoke(GET_ALL_LEADERSHIPS, SERIALIZER::decode);
}
public CompletableFuture<Set<String>> getElectedTopics(NodeId nodeId) {
- return client.submit(new GetElectedTopics(nodeId));
+ return proxy.invoke(GET_ELECTED_TOPICS, SERIALIZER::encode, new GetElectedTopics(nodeId), SERIALIZER::decode);
}
@Override
public synchronized CompletableFuture<Void> addChangeListener(Consumer<Change<Leadership>> consumer) {
if (leadershipChangeListeners.isEmpty()) {
- return client.submit(new Listen()).thenRun(() -> leadershipChangeListeners.add(consumer));
+ return proxy.invoke(ADD_LISTENER).thenRun(() -> leadershipChangeListeners.add(consumer));
} else {
leadershipChangeListeners.add(consumer);
return CompletableFuture.completedFuture(null);
@@ -185,31 +170,12 @@
@Override
public synchronized CompletableFuture<Void> removeChangeListener(Consumer<Change<Leadership>> consumer) {
if (leadershipChangeListeners.remove(consumer) && leadershipChangeListeners.isEmpty()) {
- return client.submit(new Unlisten()).thenApply(v -> null);
+ return proxy.invoke(REMOVE_LISTENER).thenApply(v -> null);
}
return CompletableFuture.completedFuture(null);
}
- @Override
- public void addStatusChangeListener(Consumer<Status> listener) {
- statusChangeListeners.add(listener);
- }
-
- @Override
- public void removeStatusChangeListener(Consumer<Status> listener) {
- statusChangeListeners.remove(listener);
- }
-
- @Override
- public Collection<Consumer<Status>> statusChangeListeners() {
- return ImmutableSet.copyOf(statusChangeListeners);
- }
-
private boolean isListening() {
return !leadershipChangeListeners.isEmpty();
}
-
- private void handleStateChange(CopycatClient.State state) {
- statusChangeListeners().forEach(listener -> listener.accept(mapper.apply(state)));
- }
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java
deleted file mode 100644
index 87e6bcc..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import java.util.Map;
-import java.util.Set;
-
-import org.onosproject.cluster.Leadership;
-import org.onosproject.cluster.NodeId;
-
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
-
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.catalyst.util.Assert;
-import io.atomix.copycat.Command;
-import io.atomix.copycat.Query;
-
-/**
- * {@link AtomixLeaderElector} resource state machine operations.
- */
-public final class AtomixLeaderElectorCommands {
-
- private AtomixLeaderElectorCommands() {
- }
-
- /**
- * Abstract election query.
- */
- @SuppressWarnings("serial")
- public abstract static class ElectionQuery<V> implements Query<V>, CatalystSerializable {
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- /**
- * Abstract election topic query.
- */
- @SuppressWarnings("serial")
- public abstract static class TopicQuery<V> extends ElectionQuery<V> implements CatalystSerializable {
- String topic;
-
- public TopicQuery() {
- }
-
- public TopicQuery(String topic) {
- this.topic = Assert.notNull(topic, "topic");
- }
-
- /**
- * Returns the topic.
- * @return topic
- */
- public String topic() {
- return topic;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- serializer.writeObject(topic, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- topic = serializer.readObject(buffer);
- }
- }
-
- /**
- * Abstract election command.
- */
- @SuppressWarnings("serial")
- public abstract static class ElectionCommand<V> implements Command<V>, CatalystSerializable {
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
- }
-
- /**
- * Listen command.
- */
- @SuppressWarnings("serial")
- public static class Listen extends ElectionCommand<Void> {
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
- }
-
- /**
- * Unlisten command.
- */
- @SuppressWarnings("serial")
- public static class Unlisten extends ElectionCommand<Void> {
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
- }
-
- /**
- * GetLeader query.
- */
- @SuppressWarnings("serial")
- public static class GetLeadership extends TopicQuery<Leadership> {
-
- public GetLeadership() {
- }
-
- public GetLeadership(String topic) {
- super(topic);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("topic", topic)
- .toString();
- }
- }
-
- /**
- * GetAllLeaders query.
- */
- @SuppressWarnings("serial")
- public static class GetAllLeaderships extends ElectionQuery<Map<String, Leadership>> {
- }
-
- /**
- * GetElectedTopics query.
- */
- @SuppressWarnings("serial")
- public static class GetElectedTopics extends ElectionQuery<Set<String>> {
- private NodeId nodeId;
-
- public GetElectedTopics() {
- }
-
- public GetElectedTopics(NodeId nodeId) {
- this.nodeId = Assert.argNot(nodeId, nodeId == null, "nodeId cannot be null");
- }
-
- /**
- * Returns the nodeId to check.
- *
- * @return The nodeId to check.
- */
- public NodeId nodeId() {
- return nodeId;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("nodeId", nodeId)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- super.writeObject(buffer, serializer);
- serializer.writeObject(nodeId, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- super.readObject(buffer, serializer);
- nodeId = serializer.readObject(buffer);
- }
- }
-
- /**
- * Enter and run for leadership.
- */
- @SuppressWarnings("serial")
- public static class Run extends ElectionCommand<Leadership> {
- private String topic;
- private NodeId nodeId;
-
- public Run() {
- }
-
- public Run(String topic, NodeId nodeId) {
- this.topic = Assert.argNot(topic, Strings.isNullOrEmpty(topic), "topic cannot be null or empty");
- this.nodeId = Assert.argNot(nodeId, nodeId == null, "nodeId cannot be null");
- }
-
- /**
- * Returns the topic.
- *
- * @return topic
- */
- public String topic() {
- return topic;
- }
-
- /**
- * Returns the nodeId.
- *
- * @return the nodeId
- */
- public NodeId nodeId() {
- return nodeId;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.SNAPSHOT;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("topic", topic)
- .add("nodeId", nodeId)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeString(topic);
- buffer.writeString(nodeId.toString());
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- topic = buffer.readString();
- nodeId = new NodeId(buffer.readString());
- }
- }
-
- /**
- * Withdraw from a leadership contest.
- */
- @SuppressWarnings("serial")
- public static class Withdraw extends ElectionCommand<Void> {
- private String topic;
-
- public Withdraw() {
- }
-
- public Withdraw(String topic) {
- this.topic = Assert.argNot(topic, Strings.isNullOrEmpty(topic), "topic cannot be null or empty");
- }
-
- /**
- * Returns the topic.
- *
- * @return The topic
- */
- public String topic() {
- return topic;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.SNAPSHOT;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("topic", topic)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeString(topic);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- topic = buffer.readString();
- }
- }
-
- /**
- * Command for administratively changing the leadership state for a node.
- */
- @SuppressWarnings("serial")
- public abstract static class ElectionChangeCommand<V> extends ElectionCommand<V> {
- private String topic;
- private NodeId nodeId;
-
- ElectionChangeCommand() {
- topic = null;
- nodeId = null;
- }
-
- public ElectionChangeCommand(String topic, NodeId nodeId) {
- this.topic = topic;
- this.nodeId = nodeId;
- }
-
- /**
- * Returns the topic.
- *
- * @return The topic
- */
- public String topic() {
- return topic;
- }
-
- /**
- * Returns the nodeId to make leader.
- *
- * @return The nodeId
- */
- public NodeId nodeId() {
- return nodeId;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.SNAPSHOT;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("topic", topic)
- .add("nodeId", nodeId)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeString(topic);
- buffer.writeString(nodeId.toString());
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- topic = buffer.readString();
- nodeId = new NodeId(buffer.readString());
- }
- }
-
- /**
- * Command for administratively anoint a node as leader.
- */
- @SuppressWarnings("serial")
- public static class Anoint extends ElectionChangeCommand<Boolean> {
-
- private Anoint() {
- }
-
- public Anoint(String topic, NodeId nodeId) {
- super(topic, nodeId);
- }
- }
-
- /**
- * Command for administratively promote a node as top candidate.
- */
- @SuppressWarnings("serial")
- public static class Promote extends ElectionChangeCommand<Boolean> {
-
- private Promote() {
- }
-
- public Promote(String topic, NodeId nodeId) {
- super(topic, nodeId);
- }
- }
-
- /**
- * Command for administratively evicting a node from all leadership topics.
- */
- @SuppressWarnings("serial")
- public static class Evict extends ElectionCommand<Void> {
- private NodeId nodeId;
-
- public Evict() {
- }
-
- public Evict(NodeId nodeId) {
- this.nodeId = nodeId;
- }
-
- /**
- * Returns the node identifier.
- *
- * @return The nodeId
- */
- public NodeId nodeId() {
- return nodeId;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.SNAPSHOT;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("nodeId", nodeId)
- .toString();
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeString(nodeId.toString());
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- nodeId = new NodeId(buffer.readString());
- }
- }
-
- /**
- * Map command type resolver.
- */
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- registry.register(Run.class, -861);
- registry.register(Withdraw.class, -862);
- registry.register(Anoint.class, -863);
- registry.register(GetAllLeaderships.class, -864);
- registry.register(GetElectedTopics.class, -865);
- registry.register(GetLeadership.class, -866);
- registry.register(Listen.class, -867);
- registry.register(Unlisten.class, -868);
- registry.register(Promote.class, -869);
- registry.register(Evict.class, -870);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java
new file mode 100644
index 0000000..b6ba8a5
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.event.EventType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Atomix leader elector events.
+ */
+public enum AtomixLeaderElectorEvents implements EventType {
+ CHANGE("change");
+
+ private final String id;
+
+ AtomixLeaderElectorEvents(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50)
+ .build("AtomixLeaderElectorEvents");
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java
deleted file mode 100644
index 22f23b5..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * {@link AtomixLeaderElector} resource factory.
- *
- */
-public class AtomixLeaderElectorFactory implements ResourceFactory<AtomixLeaderElector> {
-
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixLeaderElectorCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixLeaderElectorState(config);
- }
-
- @Override
- public AtomixLeaderElector createInstance(CopycatClient client, Properties options) {
- return new AtomixLeaderElector(client, options);
- }
-}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java
new file mode 100644
index 0000000..17b8c23
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import com.google.common.base.MoreObjects;
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link AtomixLeaderElector} resource state machine operations.
+ */
+public enum AtomixLeaderElectorOperations implements OperationId {
+ ADD_LISTENER("addListener", OperationType.COMMAND),
+ REMOVE_LISTENER("removeListener", OperationType.COMMAND),
+ RUN("run", OperationType.COMMAND),
+ WITHDRAW("withdraw", OperationType.COMMAND),
+ ANOINT("anoint", OperationType.COMMAND),
+ PROMOTE("promote", OperationType.COMMAND),
+ EVICT("evict", OperationType.COMMAND),
+ GET_LEADERSHIP("getLeadership", OperationType.QUERY),
+ GET_ALL_LEADERSHIPS("getAllLeaderships", OperationType.QUERY),
+ GET_ELECTED_TOPICS("getElectedTopics", OperationType.QUERY);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixLeaderElectorOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(Run.class)
+ .register(Withdraw.class)
+ .register(Anoint.class)
+ .register(Promote.class)
+ .register(Evict.class)
+ .register(GetLeadership.class)
+ .register(GetElectedTopics.class)
+ .build("AtomixLeaderElectorOperations");
+
+ /**
+ * Abstract election query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class ElectionOperation {
+ }
+
+ /**
+ * Abstract election topic query.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class TopicOperation extends ElectionOperation {
+ String topic;
+
+ public TopicOperation() {
+ }
+
+ public TopicOperation(String topic) {
+ this.topic = checkNotNull(topic);
+ }
+
+ /**
+ * Returns the topic.
+ * @return topic
+ */
+ public String topic() {
+ return topic;
+ }
+ }
+
+ /**
+ * GetLeader query.
+ */
+ @SuppressWarnings("serial")
+ public static class GetLeadership extends TopicOperation {
+
+ public GetLeadership() {
+ }
+
+ public GetLeadership(String topic) {
+ super(topic);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("topic", topic)
+ .toString();
+ }
+ }
+
+ /**
+ * GetElectedTopics query.
+ */
+ @SuppressWarnings("serial")
+ public static class GetElectedTopics extends ElectionOperation {
+ private NodeId nodeId;
+
+ public GetElectedTopics() {
+ }
+
+ public GetElectedTopics(NodeId nodeId) {
+ checkArgument(nodeId != null, "nodeId cannot be null");
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Returns the nodeId to check.
+ *
+ * @return The nodeId to check.
+ */
+ public NodeId nodeId() {
+ return nodeId;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("nodeId", nodeId)
+ .toString();
+ }
+ }
+
+ /**
+ * Enter and run for leadership.
+ */
+ @SuppressWarnings("serial")
+ public static class Run extends ElectionOperation {
+ private String topic;
+ private NodeId nodeId;
+
+ public Run() {
+ }
+
+ public Run(String topic, NodeId nodeId) {
+ this.topic = topic;
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Returns the topic.
+ *
+ * @return topic
+ */
+ public String topic() {
+ return topic;
+ }
+
+ /**
+ * Returns the nodeId.
+ *
+ * @return the nodeId
+ */
+ public NodeId nodeId() {
+ return nodeId;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("topic", topic)
+ .add("nodeId", nodeId)
+ .toString();
+ }
+ }
+
+ /**
+ * Withdraw from a leadership contest.
+ */
+ @SuppressWarnings("serial")
+ public static class Withdraw extends ElectionOperation {
+ private String topic;
+
+ public Withdraw() {
+ }
+
+ public Withdraw(String topic) {
+ this.topic = topic;
+ }
+
+ /**
+ * Returns the topic.
+ *
+ * @return The topic
+ */
+ public String topic() {
+ return topic;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("topic", topic)
+ .toString();
+ }
+ }
+
+ /**
+ * Command for administratively changing the leadership state for a node.
+ */
+ @SuppressWarnings("serial")
+ public abstract static class ElectionChangeOperation extends ElectionOperation {
+ private String topic;
+ private NodeId nodeId;
+
+ ElectionChangeOperation() {
+ topic = null;
+ nodeId = null;
+ }
+
+ public ElectionChangeOperation(String topic, NodeId nodeId) {
+ this.topic = topic;
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Returns the topic.
+ *
+ * @return The topic
+ */
+ public String topic() {
+ return topic;
+ }
+
+ /**
+ * Returns the nodeId to make leader.
+ *
+ * @return The nodeId
+ */
+ public NodeId nodeId() {
+ return nodeId;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("topic", topic)
+ .add("nodeId", nodeId)
+ .toString();
+ }
+ }
+
+ /**
+ * Command for administratively anoint a node as leader.
+ */
+ @SuppressWarnings("serial")
+ public static class Anoint extends ElectionChangeOperation {
+
+ private Anoint() {
+ }
+
+ public Anoint(String topic, NodeId nodeId) {
+ super(topic, nodeId);
+ }
+ }
+
+ /**
+ * Command for administratively promote a node as top candidate.
+ */
+ @SuppressWarnings("serial")
+ public static class Promote extends ElectionChangeOperation {
+
+ private Promote() {
+ }
+
+ public Promote(String topic, NodeId nodeId) {
+ super(topic, nodeId);
+ }
+ }
+
+ /**
+ * Command for administratively evicting a node from all leadership topics.
+ */
+ @SuppressWarnings("serial")
+ public static class Evict extends ElectionOperation {
+ private NodeId nodeId;
+
+ public Evict() {
+ }
+
+ public Evict(NodeId nodeId) {
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Returns the node identifier.
+ *
+ * @return The nodeId
+ */
+ public NodeId nodeId() {
+ return nodeId;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("nodeId", nodeId)
+ .toString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorService.java
similarity index 63%
rename from core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorState.java
rename to core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorService.java
index 369c191..c65d920 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorState.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorService.java
@@ -15,18 +15,6 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import com.google.common.collect.ImmutableSet;
-import io.atomix.copycat.server.session.ServerSession;
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.Snapshottable;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.session.SessionListener;
-import io.atomix.copycat.server.storage.snapshot.SnapshotReader;
-import io.atomix.copycat.server.storage.snapshot.SnapshotWriter;
-import io.atomix.resource.ResourceStateMachine;
-
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -34,69 +22,101 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.session.RaftSession;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
import org.onosproject.cluster.Leader;
import org.onosproject.cluster.Leadership;
import org.onosproject.cluster.NodeId;
import org.onosproject.event.Change;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Anoint;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Evict;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetAllLeaderships;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetElectedTopics;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetLeadership;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Listen;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Promote;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Run;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Unlisten;
-import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Withdraw;
-import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Anoint;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Evict;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetElectedTopics;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetLeadership;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Promote;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Run;
+import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Withdraw;
import org.onosproject.store.service.Serializer;
-import org.slf4j.Logger;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorEvents.CHANGE;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ADD_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ANOINT;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.EVICT;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ALL_LEADERSHIPS;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ELECTED_TOPICS;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_LEADERSHIP;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.PROMOTE;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.REMOVE_LISTENER;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.RUN;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.WITHDRAW;
/**
* State machine for {@link AtomixLeaderElector} resource.
*/
-public class AtomixLeaderElectorState extends ResourceStateMachine
- implements SessionListener, Snapshottable {
+public class AtomixLeaderElectorService extends AbstractRaftService {
- private final Logger log = getLogger(getClass());
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(AtomixLeaderElectorOperations.NAMESPACE)
+ .register(AtomixLeaderElectorEvents.NAMESPACE)
+ .register(ElectionState.class)
+ .register(Registration.class)
+ .register(new LinkedHashMap<>().keySet().getClass())
+ .build());
+
private Map<String, AtomicLong> termCounters = new HashMap<>();
private Map<String, ElectionState> elections = new HashMap<>();
- private final Map<Long, Commit<? extends Listen>> listeners = new LinkedHashMap<>();
- private final Serializer serializer = Serializer.using(Arrays.asList(KryoNamespaces.API),
- ElectionState.class,
- Registration.class);
+ private Map<Long, RaftSession> listeners = new LinkedHashMap<>();
- public AtomixLeaderElectorState(Properties properties) {
- super(properties);
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeObject(Sets.newHashSet(listeners.keySet()), SERIALIZER::encode);
+ writer.writeObject(termCounters, SERIALIZER::encode);
+ writer.writeObject(elections, SERIALIZER::encode);
+ getLogger().debug("Took state machine snapshot");
}
@Override
- protected void configure(StateMachineExecutor executor) {
+ public void install(SnapshotReader reader) {
+ listeners = new LinkedHashMap<>();
+ for (Long sessionId : reader.<Set<Long>>readObject(SERIALIZER::decode)) {
+ listeners.put(sessionId, getSessions().getSession(sessionId));
+ }
+ termCounters = reader.readObject(SERIALIZER::decode);
+ elections = reader.readObject(SERIALIZER::decode);
+ getLogger().debug("Reinstated state machine from snapshot");
+ }
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
// Notification
- executor.register(Listen.class, this::listen);
- executor.register(Unlisten.class, this::unlisten);
+ executor.register(ADD_LISTENER, this::listen);
+ executor.register(REMOVE_LISTENER, this::unlisten);
// Commands
- executor.register(Run.class, this::run);
- executor.register(Withdraw.class, this::withdraw);
- executor.register(Anoint.class, this::anoint);
- executor.register(Promote.class, this::promote);
- executor.register(Evict.class, this::evict);
+ executor.register(RUN, SERIALIZER::decode, this::run, SERIALIZER::encode);
+ executor.register(WITHDRAW, SERIALIZER::decode, this::withdraw);
+ executor.register(ANOINT, SERIALIZER::decode, this::anoint, SERIALIZER::encode);
+ executor.register(PROMOTE, SERIALIZER::decode, this::promote, SERIALIZER::encode);
+ executor.register(EVICT, SERIALIZER::decode, this::evict);
// Queries
- executor.register(GetLeadership.class, this::leadership);
- executor.register(GetAllLeaderships.class, this::allLeaderships);
- executor.register(GetElectedTopics.class, this::electedTopics);
+ executor.register(GET_LEADERSHIP, SERIALIZER::decode, this::getLeadership, SERIALIZER::encode);
+ executor.register(GET_ALL_LEADERSHIPS, this::allLeaderships, SERIALIZER::encode);
+ executor.register(GET_ELECTED_TOPICS, SERIALIZER::decode, this::electedTopics, SERIALIZER::encode);
}
private void notifyLeadershipChange(Leadership previousLeadership, Leadership newLeadership) {
@@ -107,16 +127,7 @@
if (changes.isEmpty()) {
return;
}
- listeners.values()
- .forEach(listener -> listener.session()
- .publish(AtomixLeaderElector.CHANGE_SUBJECT, changes));
- }
-
- @Override
- public void delete() {
- // Close and clear Listeners
- listeners.values().forEach(Commit::close);
- listeners.clear();
+ listeners.values().forEach(session -> session.publish(CHANGE, SERIALIZER::encode, changes));
}
/**
@@ -124,22 +135,8 @@
*
* @param commit listen commit
*/
- public void listen(Commit<? extends Listen> commit) {
- Long sessionId = commit.session().id();
- if (listeners.putIfAbsent(commit.session().id(), commit) != null) {
- commit.close();
- }
- commit.session()
- .onStateChange(
- state -> {
- if (state == ServerSession.State.CLOSED
- || state == ServerSession.State.EXPIRED) {
- Commit<? extends Listen> listener = listeners.remove(sessionId);
- if (listener != null) {
- listener.close();
- }
- }
- });
+ public void listen(Commit<Void> commit) {
+ listeners.put(commit.session().sessionId().id(), commit.session());
}
/**
@@ -147,27 +144,20 @@
*
* @param commit unlisten commit
*/
- public void unlisten(Commit<? extends Unlisten> commit) {
- try {
- Commit<? extends Listen> listener = listeners.remove(commit.session().id());
- if (listener != null) {
- listener.close();
- }
- } finally {
- commit.close();
- }
+ public void unlisten(Commit<Void> commit) {
+ listeners.remove(commit.session().sessionId().id());
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.Run} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.Run} commit.
* @param commit commit entry
* @return topic leader. If no previous leader existed this is the node that just entered the race.
*/
public Leadership run(Commit<? extends Run> commit) {
try {
- String topic = commit.operation().topic();
+ String topic = commit.value().topic();
Leadership oldLeadership = leadership(topic);
- Registration registration = new Registration(commit.operation().nodeId(), commit.session().id());
+ Registration registration = new Registration(commit.value().nodeId(), commit.session().sessionId().id());
elections.compute(topic, (k, v) -> {
if (v == null) {
return new ElectionState(registration, termCounter(topic)::incrementAndGet);
@@ -186,44 +176,40 @@
}
return newLeadership;
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.Withdraw} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.Withdraw} commit.
* @param commit withdraw commit
*/
public void withdraw(Commit<? extends Withdraw> commit) {
try {
- String topic = commit.operation().topic();
+ String topic = commit.value().topic();
Leadership oldLeadership = leadership(topic);
elections.computeIfPresent(topic, (k, v) -> v.cleanup(commit.session(),
- termCounter(topic)::incrementAndGet));
+ termCounter(topic)::incrementAndGet));
Leadership newLeadership = leadership(topic);
if (!Objects.equal(oldLeadership, newLeadership)) {
notifyLeadershipChange(oldLeadership, newLeadership);
}
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.Anoint} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.Anoint} commit.
* @param commit anoint commit
* @return {@code true} if changes were made and the transfer occurred; {@code false} if it did not.
*/
public boolean anoint(Commit<? extends Anoint> commit) {
try {
- String topic = commit.operation().topic();
- NodeId nodeId = commit.operation().nodeId();
+ String topic = commit.value().topic();
+ NodeId nodeId = commit.value().nodeId();
Leadership oldLeadership = leadership(topic);
ElectionState electionState = elections.computeIfPresent(topic,
(k, v) -> v.transferLeadership(nodeId, termCounter(topic)));
@@ -233,24 +219,22 @@
}
return (electionState != null &&
electionState.leader() != null &&
- commit.operation().nodeId().equals(electionState.leader().nodeId()));
+ commit.value().nodeId().equals(electionState.leader().nodeId()));
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.Promote} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.Promote} commit.
* @param commit promote commit
* @return {@code true} if changes desired end state is achieved.
*/
public boolean promote(Commit<? extends Promote> commit) {
try {
- String topic = commit.operation().topic();
- NodeId nodeId = commit.operation().nodeId();
+ String topic = commit.value().topic();
+ NodeId nodeId = commit.value().nodeId();
Leadership oldLeadership = leadership(topic);
if (oldLeadership == null || !oldLeadership.candidates().contains(nodeId)) {
return false;
@@ -262,21 +246,19 @@
}
return true;
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.Evict} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.Evict} commit.
* @param commit evict commit
*/
public void evict(Commit<? extends Evict> commit) {
try {
List<Change<Leadership>> changes = Lists.newArrayList();
- NodeId nodeId = commit.operation().nodeId();
+ NodeId nodeId = commit.value().nodeId();
Set<String> topics = Maps.filterValues(elections, e -> e.candidates().contains(nodeId)).keySet();
topics.forEach(topic -> {
Leadership oldLeadership = leadership(topic);
@@ -288,65 +270,57 @@
});
notifyLeadershipChanges(changes);
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.GetLeadership} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.GetLeadership} commit.
* @param commit GetLeadership commit
* @return leader
*/
- public Leadership leadership(Commit<? extends GetLeadership> commit) {
- String topic = commit.operation().topic();
+ public Leadership getLeadership(Commit<? extends GetLeadership> commit) {
+ String topic = commit.value().topic();
try {
return leadership(topic);
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.GetElectedTopics} commit.
+ * Applies an {@link AtomixLeaderElectorOperations.GetElectedTopics} commit.
* @param commit commit entry
* @return set of topics for which the node is the leader
*/
public Set<String> electedTopics(Commit<? extends GetElectedTopics> commit) {
try {
- NodeId nodeId = commit.operation().nodeId();
+ NodeId nodeId = commit.value().nodeId();
return ImmutableSet.copyOf(Maps.filterEntries(elections, e -> {
Leader leader = leadership(e.getKey()).leader();
return leader != null && leader.nodeId().equals(nodeId);
}).keySet());
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
/**
- * Applies an {@link AtomixLeaderElectorCommands.GetAllLeaderships} commit.
+ * Applies an {@link AtomixLeaderElectorOperations#GET_ALL_LEADERSHIPS} commit.
* @param commit GetAllLeaderships commit
* @return topic to leader mapping
*/
- public Map<String, Leadership> allLeaderships(Commit<? extends GetAllLeaderships> commit) {
+ public Map<String, Leadership> allLeaderships(Commit<Void> commit) {
Map<String, Leadership> result = new HashMap<>();
try {
result.putAll(Maps.transformEntries(elections, (k, v) -> leadership(k)));
return result;
} catch (Exception e) {
- log.error("State machine operation failed", e);
+ getLogger().error("State machine operation failed", e);
throw Throwables.propagate(e);
- } finally {
- commit.close();
}
}
@@ -366,11 +340,8 @@
return electionState == null ? new LinkedList<>() : electionState.candidates();
}
- private void onSessionEnd(ServerSession session) {
- Commit<? extends AtomixLeaderElectorCommands.Listen> listener = listeners.remove(session.id());
- if (listener != null) {
- listener.close();
- }
+ private void onSessionEnd(RaftSession session) {
+ listeners.remove(session.sessionId().id());
Set<String> topics = elections.keySet();
List<Change<Leadership>> changes = Lists.newArrayList();
topics.forEach(topic -> {
@@ -440,15 +411,15 @@
this.termStartTime = termStartTime;
}
- public ElectionState cleanup(ServerSession session, Supplier<Long> termCounter) {
+ public ElectionState cleanup(RaftSession session, Supplier<Long> termCounter) {
Optional<Registration> registration =
- registrations.stream().filter(r -> r.sessionId() == session.id()).findFirst();
+ registrations.stream().filter(r -> r.sessionId() == session.sessionId().id()).findFirst();
if (registration.isPresent()) {
List<Registration> updatedRegistrations =
registrations.stream()
- .filter(r -> r.sessionId() != session.id())
- .collect(Collectors.toList());
- if (leader.sessionId() == session.id()) {
+ .filter(r -> r.sessionId() != session.sessionId().id())
+ .collect(Collectors.toList());
+ if (leader.sessionId() == session.sessionId().id()) {
if (!updatedRegistrations.isEmpty()) {
return new ElectionState(updatedRegistrations,
updatedRegistrations.get(0),
@@ -471,8 +442,8 @@
if (registration.isPresent()) {
List<Registration> updatedRegistrations =
registrations.stream()
- .filter(r -> !r.nodeId().equals(nodeId))
- .collect(Collectors.toList());
+ .filter(r -> !r.nodeId().equals(nodeId))
+ .collect(Collectors.toList());
if (leader.nodeId().equals(nodeId)) {
if (!updatedRegistrations.isEmpty()) {
return new ElectionState(updatedRegistrations,
@@ -522,14 +493,14 @@
public ElectionState transferLeadership(NodeId nodeId, AtomicLong termCounter) {
Registration newLeader = registrations.stream()
- .filter(r -> r.nodeId().equals(nodeId))
- .findFirst()
- .orElse(null);
+ .filter(r -> r.nodeId().equals(nodeId))
+ .findFirst()
+ .orElse(null);
if (newLeader != null) {
return new ElectionState(registrations,
- newLeader,
- termCounter.incrementAndGet(),
- System.currentTimeMillis());
+ newLeader,
+ termCounter.incrementAndGet(),
+ System.currentTimeMillis());
} else {
return this;
}
@@ -537,66 +508,33 @@
public ElectionState promote(NodeId nodeId) {
Registration registration = registrations.stream()
- .filter(r -> r.nodeId().equals(nodeId))
- .findFirst()
- .orElse(null);
+ .filter(r -> r.nodeId().equals(nodeId))
+ .findFirst()
+ .orElse(null);
List<Registration> updatedRegistrations = Lists.newArrayList();
updatedRegistrations.add(registration);
registrations.stream()
- .filter(r -> !r.nodeId().equals(nodeId))
- .forEach(updatedRegistrations::add);
+ .filter(r -> !r.nodeId().equals(nodeId))
+ .forEach(updatedRegistrations::add);
return new ElectionState(updatedRegistrations,
- leader,
- term,
- termStartTime);
+ leader,
+ term,
+ termStartTime);
}
}
@Override
- public void register(ServerSession session) {
- }
-
- @Override
- public void unregister(ServerSession session) {
+ public void onExpire(RaftSession session) {
onSessionEnd(session);
}
@Override
- public void expire(ServerSession session) {
+ public void onClose(RaftSession session) {
onSessionEnd(session);
}
- @Override
- public void close(ServerSession session) {
- onSessionEnd(session);
- }
-
- @Override
- public void snapshot(SnapshotWriter writer) {
- byte[] encodedTermCounters = serializer.encode(termCounters);
- writer.writeInt(encodedTermCounters.length);
- writer.write(encodedTermCounters);
- byte[] encodedElections = serializer.encode(elections);
- writer.writeInt(encodedElections.length);
- writer.write(encodedElections);
- log.debug("Took state machine snapshot");
- }
-
- @Override
- public void install(SnapshotReader reader) {
- int encodedTermCountersSize = reader.readInt();
- byte[] encodedTermCounters = new byte[encodedTermCountersSize];
- reader.read(encodedTermCounters);
- termCounters = serializer.decode(encodedTermCounters);
- int encodedElectionsSize = reader.readInt();
- byte[] encodedElections = new byte[encodedElectionsSize];
- reader.read(encodedElections);
- elections = serializer.decode(encodedElections);
- log.debug("Reinstated state machine from snapshot");
- }
-
private AtomicLong termCounter(String topic) {
return termCounters.computeIfAbsent(topic, k -> new AtomicLong(0));
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java
new file mode 100644
index 0000000..7eafdb6
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import org.onosproject.store.service.Serializer;
+
+/**
+ * ONOS to Atomix serializer adapter.
+ */
+public class AtomixSerializerAdapter implements io.atomix.serializer.Serializer {
+ private final Serializer serializer;
+
+ public AtomixSerializerAdapter(Serializer serializer) {
+ this.serializer = serializer;
+ }
+
+ @Override
+ public <T> byte[] encode(T object) {
+ return serializer.encode(object);
+ }
+
+ @Override
+ public <T> T decode(byte[] bytes) {
+ return serializer.decode(bytes);
+ }
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java
index 569a597..e21ec8c 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java
@@ -15,16 +15,8 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.slf4j.LoggerFactory.getLogger;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.AbstractResource;
-import io.atomix.resource.ResourceTypeInfo;
-
import java.util.Collection;
import java.util.List;
-import java.util.Properties;
import java.util.Timer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@@ -34,63 +26,64 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import com.google.common.collect.ImmutableList;
+import io.atomix.protocols.raft.proxy.RaftProxy;
import org.onlab.util.AbstractAccumulator;
import org.onlab.util.Accumulator;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Add;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Complete;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Register;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Stats;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Take;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Unregister;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Add;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Complete;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Take;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.Task;
import org.onosproject.store.service.WorkQueue;
import org.onosproject.store.service.WorkQueueStats;
import org.slf4j.Logger;
-import com.google.common.collect.ImmutableList;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueEvents.TASK_AVAILABLE;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.COMPLETE;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.REGISTER;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.STATS;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.TAKE;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.UNREGISTER;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Distributed resource providing the {@link WorkQueue} primitive.
*/
-@ResourceTypeInfo(id = -154, factory = AtomixWorkQueueFactory.class)
-public class AtomixWorkQueue extends AbstractResource<AtomixWorkQueue>
- implements WorkQueue<byte[]> {
+public class AtomixWorkQueue extends AbstractRaftPrimitive implements WorkQueue<byte[]> {
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixWorkQueueOperations.NAMESPACE)
+ .register(AtomixWorkQueueEvents.NAMESPACE)
+ .build());
private final Logger log = getLogger(getClass());
- public static final String TASK_AVAILABLE = "task-available";
private final ExecutorService executor = newSingleThreadExecutor(groupedThreads("AtomixWorkQueue", "%d", log));
private final AtomicReference<TaskProcessor> taskProcessor = new AtomicReference<>();
private final Timer timer = new Timer("atomix-work-queue-completer");
private final AtomicBoolean isRegistered = new AtomicBoolean(false);
- protected AtomixWorkQueue(CopycatClient client, Properties options) {
- super(client, options);
- }
-
- @Override
- public String name() {
- return null;
+ public AtomixWorkQueue(RaftProxy proxy) {
+ super(proxy);
+ proxy.addStateChangeListener(state -> {
+ if (state == RaftProxy.State.CONNECTED && isRegistered.get()) {
+ proxy.invoke(REGISTER);
+ }
+ });
+ proxy.addEventListener(TASK_AVAILABLE, this::resumeWork);
}
@Override
public CompletableFuture<Void> destroy() {
executor.shutdown();
timer.cancel();
- return client.submit(new Clear());
- }
-
- @Override
- public CompletableFuture<AtomixWorkQueue> open() {
- return super.open().thenApply(result -> {
- client.onStateChange(state -> {
- if (state == CopycatClient.State.CONNECTED && isRegistered.get()) {
- client.submit(new Register());
- }
- });
- client.onEvent(TASK_AVAILABLE, this::resumeWork);
- return result;
- });
+ return proxy.invoke(CLEAR);
}
@Override
@@ -98,7 +91,7 @@
if (items.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
- return client.submit(new Add(items));
+ return proxy.invoke(ADD, SERIALIZER::encode, new Add(items));
}
@Override
@@ -106,7 +99,7 @@
if (maxTasks <= 0) {
return CompletableFuture.completedFuture(ImmutableList.of());
}
- return client.submit(new Take(maxTasks));
+ return proxy.invoke(TAKE, SERIALIZER::encode, new Take(maxTasks), SERIALIZER::decode);
}
@Override
@@ -114,21 +107,21 @@
if (taskIds.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
- return client.submit(new Complete(taskIds));
+ return proxy.invoke(COMPLETE, SERIALIZER::encode, new Complete(taskIds));
}
@Override
public CompletableFuture<Void> registerTaskProcessor(Consumer<byte[]> callback,
- int parallelism,
- Executor executor) {
+ int parallelism,
+ Executor executor) {
Accumulator<String> completedTaskAccumulator =
new CompletedTaskAccumulator(timer, 50, 50); // TODO: make configurable
taskProcessor.set(new TaskProcessor(callback,
- parallelism,
- executor,
- completedTaskAccumulator));
+ parallelism,
+ executor,
+ completedTaskAccumulator));
return register().thenCompose(v -> take(parallelism))
- .thenAccept(taskProcessor.get());
+ .thenAccept(taskProcessor.get());
}
@Override
@@ -138,7 +131,7 @@
@Override
public CompletableFuture<WorkQueueStats> stats() {
- return client.submit(new Stats());
+ return proxy.invoke(STATS, SERIALIZER::decode);
}
private void resumeWork() {
@@ -147,15 +140,15 @@
return;
}
this.take(activeProcessor.headRoom())
- .whenCompleteAsync((tasks, e) -> activeProcessor.accept(tasks), executor);
+ .whenCompleteAsync((tasks, e) -> activeProcessor.accept(tasks), executor);
}
private CompletableFuture<Void> register() {
- return client.submit(new Register()).thenRun(() -> isRegistered.set(true));
+ return proxy.invoke(REGISTER).thenRun(() -> isRegistered.set(true));
}
private CompletableFuture<Void> unregister() {
- return client.submit(new Unregister()).thenRun(() -> isRegistered.set(false));
+ return proxy.invoke(UNREGISTER).thenRun(() -> isRegistered.set(false));
}
// TaskId accumulator for paced triggering of task completion calls.
@@ -178,9 +171,9 @@
private final Accumulator<String> taskCompleter;
public TaskProcessor(Consumer<byte[]> backingConsumer,
- int parallelism,
- Executor executor,
- Accumulator<String> taskCompleter) {
+ int parallelism,
+ Executor executor,
+ Accumulator<String> taskCompleter) {
this.backingConsumer = backingConsumer;
this.headRoom = new AtomicInteger(parallelism);
this.executor = executor;
@@ -198,17 +191,17 @@
}
headRoom.addAndGet(-1 * tasks.size());
tasks.forEach(task ->
- executor.execute(() -> {
- try {
- backingConsumer.accept(task.payload());
- taskCompleter.add(task.taskId());
- } catch (Exception e) {
- log.debug("Task execution failed", e);
- } finally {
- headRoom.incrementAndGet();
- resumeWork();
- }
- }));
+ executor.execute(() -> {
+ try {
+ backingConsumer.accept(task.payload());
+ taskCompleter.add(task.taskId());
+ } catch (Exception e) {
+ log.debug("Task execution failed", e);
+ } finally {
+ headRoom.incrementAndGet();
+ resumeWork();
+ }
+ }));
}
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java
deleted file mode 100644
index 977470d..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.buffer.BufferInput;
-import io.atomix.catalyst.buffer.BufferOutput;
-import io.atomix.catalyst.serializer.CatalystSerializable;
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.serializer.SerializerRegistry;
-import io.atomix.copycat.Command;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import org.onosproject.store.service.Task;
-import org.onosproject.store.service.WorkQueueStats;
-
-import com.google.common.base.MoreObjects;
-
-/**
- * {@link AtomixWorkQueue} resource state machine operations.
- */
-public final class AtomixWorkQueueCommands {
-
- private AtomixWorkQueueCommands() {
- }
-
- /**
- * Command to add a collection of tasks to the queue.
- */
- @SuppressWarnings("serial")
- public static class Add implements Command<Void>, CatalystSerializable {
-
- private Collection<byte[]> items;
-
- private Add() {
- }
-
- public Add(Collection<byte[]> items) {
- this.items = items;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeInt(items.size());
- items.forEach(task -> serializer.writeObject(task, buffer));
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- items = IntStream.range(0, buffer.readInt())
- .mapToObj(i -> serializer.<byte[]>readObject(buffer))
- .collect(Collectors.toCollection(ArrayList::new));
- }
-
- public Collection<byte[]> items() {
- return items;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("items", items)
- .toString();
- }
- }
-
- /**
- * Command to take a task from the queue.
- */
- @SuppressWarnings("serial")
- public static class Take implements Command<Collection<Task<byte[]>>>, CatalystSerializable {
-
- private int maxTasks;
-
- private Take() {
- }
-
- public Take(int maxTasks) {
- this.maxTasks = maxTasks;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- buffer.writeInt(maxTasks);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- maxTasks = buffer.readInt();
- }
-
- public int maxTasks() {
- return maxTasks;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("maxTasks", maxTasks)
- .toString();
- }
- }
-
- @SuppressWarnings("serial")
- public static class Stats implements Command<WorkQueueStats>, CatalystSerializable {
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
- }
-
-
-
- @SuppressWarnings("serial")
- public static class Register implements Command<Void>, CatalystSerializable {
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.QUORUM;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
- }
-
- @SuppressWarnings("serial")
- public static class Unregister implements Command<Void>, CatalystSerializable {
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
- }
-
- @SuppressWarnings("serial")
- public static class Complete implements Command<Void>, CatalystSerializable {
- private Collection<String> taskIds;
-
- private Complete() {
- }
-
- public Complete(Collection<String> taskIds) {
- this.taskIds = taskIds;
- }
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- serializer.writeObject(taskIds, buffer);
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- taskIds = serializer.readObject(buffer);
- }
-
- public Collection<String> taskIds() {
- return taskIds;
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("taskIds", taskIds)
- .toString();
- }
- }
-
- @SuppressWarnings("serial")
- public static class Clear implements Command<Void>, CatalystSerializable {
-
- @Override
- public void writeObject(BufferOutput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public void readObject(BufferInput<?> buffer, Serializer serializer) {
- }
-
- @Override
- public CompactionMode compaction() {
- return CompactionMode.TOMBSTONE;
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .toString();
- }
- }
-
- /**
- * Work queue command type resolver.
- */
- public static class TypeResolver implements SerializableTypeResolver {
- @Override
- public void resolve(SerializerRegistry registry) {
- registry.register(Register.class, -960);
- registry.register(Unregister.class, -961);
- registry.register(Take.class, -962);
- registry.register(Add.class, -963);
- registry.register(Complete.class, -964);
- registry.register(Stats.class, -965);
- registry.register(Clear.class, -966);
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java
new file mode 100644
index 0000000..50b7366
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.event.EventType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Atomix work queue events.
+ */
+public enum AtomixWorkQueueEvents implements EventType {
+ TASK_AVAILABLE("taskAvailable");
+
+ private final String id;
+
+ AtomixWorkQueueEvents(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50)
+ .build("AtomixWorkQueueEvents");
+}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java
deleted file mode 100644
index 0c61b2e..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import io.atomix.catalyst.serializer.SerializableTypeResolver;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.resource.ResourceFactory;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.Properties;
-
-/**
- * {@link AtomixWorkQueue} resource factory.
- */
-public class AtomixWorkQueueFactory implements ResourceFactory<AtomixWorkQueue> {
-
- @Override
- public SerializableTypeResolver createSerializableTypeResolver() {
- return new AtomixWorkQueueCommands.TypeResolver();
- }
-
- @Override
- public ResourceStateMachine createStateMachine(Properties config) {
- return new AtomixWorkQueueState(config);
- }
-
- @Override
- public AtomixWorkQueue createInstance(CopycatClient client, Properties properties) {
- return new AtomixWorkQueue(client, properties);
- }
-}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java
new file mode 100644
index 0000000..a7a0df1
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Collection;
+
+import com.google.common.base.MoreObjects;
+import io.atomix.protocols.raft.operation.OperationId;
+import io.atomix.protocols.raft.operation.OperationType;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Task;
+import org.onosproject.store.service.WorkQueueStats;
+
+/**
+ * {@link AtomixWorkQueue} resource state machine operations.
+ */
+public enum AtomixWorkQueueOperations implements OperationId {
+ STATS("stats", OperationType.QUERY),
+ REGISTER("register", OperationType.COMMAND),
+ UNREGISTER("unregister", OperationType.COMMAND),
+ ADD("add", OperationType.COMMAND),
+ TAKE("take", OperationType.COMMAND),
+ COMPLETE("complete", OperationType.COMMAND),
+ CLEAR("clear", OperationType.COMMAND);
+
+ private final String id;
+ private final OperationType type;
+
+ AtomixWorkQueueOperations(String id, OperationType type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public OperationType type() {
+ return type;
+ }
+
+ public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .register(Add.class)
+ .register(Take.class)
+ .register(Complete.class)
+ .register(Task.class)
+ .register(WorkQueueStats.class)
+ .build("AtomixWorkQueueOperations");
+
+ /**
+ * Work queue operation.
+ */
+ public abstract static class WorkQueueOperation {
+ }
+
+ /**
+ * Command to add a collection of tasks to the queue.
+ */
+ @SuppressWarnings("serial")
+ public static class Add extends WorkQueueOperation {
+ private Collection<byte[]> items;
+
+ private Add() {
+ }
+
+ public Add(Collection<byte[]> items) {
+ this.items = items;
+ }
+
+ public Collection<byte[]> items() {
+ return items;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("items", items)
+ .toString();
+ }
+ }
+
+ /**
+ * Command to take a task from the queue.
+ */
+ @SuppressWarnings("serial")
+ public static class Take extends WorkQueueOperation {
+ private int maxTasks;
+
+ private Take() {
+ }
+
+ public Take(int maxTasks) {
+ this.maxTasks = maxTasks;
+ }
+
+ public int maxTasks() {
+ return maxTasks;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("maxTasks", maxTasks)
+ .toString();
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class Complete extends WorkQueueOperation {
+ private Collection<String> taskIds;
+
+ private Complete() {
+ }
+
+ public Complete(Collection<String> taskIds) {
+ this.taskIds = taskIds;
+ }
+
+ public Collection<String> taskIds() {
+ return taskIds;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("taskIds", taskIds)
+ .toString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java
new file mode 100644
index 0000000..6458ec8
--- /dev/null
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import io.atomix.protocols.raft.service.AbstractRaftService;
+import io.atomix.protocols.raft.service.Commit;
+import io.atomix.protocols.raft.service.RaftServiceExecutor;
+import io.atomix.protocols.raft.session.RaftSession;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Add;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Complete;
+import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Take;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Task;
+import org.onosproject.store.service.WorkQueueStats;
+
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueEvents.TASK_AVAILABLE;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.CLEAR;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.COMPLETE;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.REGISTER;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.STATS;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.TAKE;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.UNREGISTER;
+
+/**
+ * State machine for {@link AtomixWorkQueue} resource.
+ */
+public class AtomixWorkQueueService extends AbstractRaftService {
+
+ private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.BASIC)
+ .register(AtomixWorkQueueOperations.NAMESPACE)
+ .register(AtomixWorkQueueEvents.NAMESPACE)
+ .register(TaskAssignment.class)
+ .register(new HashMap().keySet().getClass())
+ .register(ArrayDeque.class)
+ .build());
+
+ private final AtomicLong totalCompleted = new AtomicLong(0);
+
+ private Queue<Task<byte[]>> unassignedTasks = Queues.newArrayDeque();
+ private Map<String, TaskAssignment> assignments = Maps.newHashMap();
+ private Map<Long, RaftSession> registeredWorkers = Maps.newHashMap();
+
+ @Override
+ public void snapshot(SnapshotWriter writer) {
+ writer.writeObject(Sets.newHashSet(registeredWorkers.keySet()), SERIALIZER::encode);
+ writer.writeObject(assignments, SERIALIZER::encode);
+ writer.writeObject(unassignedTasks, SERIALIZER::encode);
+ writer.writeLong(totalCompleted.get());
+ }
+
+ @Override
+ public void install(SnapshotReader reader) {
+ registeredWorkers = Maps.newHashMap();
+ for (Long sessionId : reader.<Set<Long>>readObject(SERIALIZER::decode)) {
+ registeredWorkers.put(sessionId, getSessions().getSession(sessionId));
+ }
+ assignments = reader.readObject(SERIALIZER::decode);
+ unassignedTasks = reader.readObject(SERIALIZER::decode);
+ totalCompleted.set(reader.readLong());
+ }
+
+ @Override
+ protected void configure(RaftServiceExecutor executor) {
+ executor.register(STATS, this::stats, SERIALIZER::encode);
+ executor.register(REGISTER, this::register);
+ executor.register(UNREGISTER, this::unregister);
+ executor.register(ADD, SERIALIZER::decode, this::add);
+ executor.register(TAKE, SERIALIZER::decode, this::take, SERIALIZER::encode);
+ executor.register(COMPLETE, SERIALIZER::decode, this::complete);
+ executor.register(CLEAR, this::clear);
+ }
+
+ protected WorkQueueStats stats(Commit<Void> commit) {
+ return WorkQueueStats.builder()
+ .withTotalCompleted(totalCompleted.get())
+ .withTotalPending(unassignedTasks.size())
+ .withTotalInProgress(assignments.size())
+ .build();
+ }
+
+ protected void clear(Commit<Void> commit) {
+ unassignedTasks.clear();
+ assignments.clear();
+ registeredWorkers.clear();
+ totalCompleted.set(0);
+ }
+
+ protected void register(Commit<Void> commit) {
+ registeredWorkers.put(commit.session().sessionId().id(), commit.session());
+ }
+
+ protected void unregister(Commit<Void> commit) {
+ registeredWorkers.remove(commit.session().sessionId().id());
+ }
+
+ protected void add(Commit<? extends Add> commit) {
+ Collection<byte[]> items = commit.value().items();
+
+ AtomicInteger itemIndex = new AtomicInteger(0);
+ items.forEach(item -> {
+ String taskId = String.format("%d:%d:%d", commit.session().sessionId().id(),
+ commit.index(),
+ itemIndex.getAndIncrement());
+ unassignedTasks.add(new Task<>(taskId, item));
+ });
+
+ // Send an event to all sessions that have expressed interest in task processing
+ // and are not actively processing a task.
+ registeredWorkers.values().forEach(session -> session.publish(TASK_AVAILABLE));
+ // FIXME: This generates a lot of event traffic.
+ }
+
+ protected Collection<Task<byte[]>> take(Commit<? extends Take> commit) {
+ try {
+ if (unassignedTasks.isEmpty()) {
+ return ImmutableList.of();
+ }
+ long sessionId = commit.session().sessionId().id();
+ int maxTasks = commit.value().maxTasks();
+ return IntStream.range(0, Math.min(maxTasks, unassignedTasks.size()))
+ .mapToObj(i -> {
+ Task<byte[]> task = unassignedTasks.poll();
+ String taskId = task.taskId();
+ TaskAssignment assignment = new TaskAssignment(sessionId, task);
+
+ // bookkeeping
+ assignments.put(taskId, assignment);
+
+ return task;
+ })
+ .collect(Collectors.toCollection(ArrayList::new));
+ } catch (Exception e) {
+ getLogger().warn("State machine update failed", e);
+ throw Throwables.propagate(e);
+ }
+ }
+
+ protected void complete(Commit<? extends Complete> commit) {
+ long sessionId = commit.session().sessionId().id();
+ try {
+ commit.value().taskIds().forEach(taskId -> {
+ TaskAssignment assignment = assignments.get(taskId);
+ if (assignment != null && assignment.sessionId() == sessionId) {
+ assignments.remove(taskId);
+ // bookkeeping
+ totalCompleted.incrementAndGet();
+ }
+ });
+ } catch (Exception e) {
+ getLogger().warn("State machine update failed", e);
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public void onExpire(RaftSession session) {
+ evictWorker(session.sessionId().id());
+ }
+
+ @Override
+ public void onClose(RaftSession session) {
+ evictWorker(session.sessionId().id());
+ }
+
+ private void evictWorker(long sessionId) {
+ registeredWorkers.remove(sessionId);
+
+ // TODO: Maintain an index of tasks by session for efficient access.
+ Iterator<Map.Entry<String, TaskAssignment>> iter = assignments.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<String, TaskAssignment> entry = iter.next();
+ TaskAssignment assignment = entry.getValue();
+ if (assignment.sessionId() == sessionId) {
+ unassignedTasks.add(assignment.task());
+ iter.remove();
+ }
+ }
+ }
+
+ private static class TaskAssignment {
+ private final long sessionId;
+ private final Task<byte[]> task;
+
+ public TaskAssignment(long sessionId, Task<byte[]> task) {
+ this.sessionId = sessionId;
+ this.task = task;
+ }
+
+ public long sessionId() {
+ return sessionId;
+ }
+
+ public Task<byte[]> task() {
+ return task;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("sessionId", sessionId)
+ .add("task", task)
+ .toString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java
deleted file mode 100644
index 82f28e8..0000000
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.resources.impl;
-
-import static org.slf4j.LoggerFactory.getLogger;
-import io.atomix.copycat.server.Commit;
-import io.atomix.copycat.server.Snapshottable;
-import io.atomix.copycat.server.StateMachineExecutor;
-import io.atomix.copycat.server.session.ServerSession;
-import io.atomix.copycat.server.session.SessionListener;
-import io.atomix.copycat.server.storage.snapshot.SnapshotReader;
-import io.atomix.copycat.server.storage.snapshot.SnapshotWriter;
-import io.atomix.resource.ResourceStateMachine;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import org.onlab.util.CountDownCompleter;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Add;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Clear;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Complete;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Register;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Stats;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Take;
-import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Unregister;
-import org.onosproject.store.service.Task;
-import org.onosproject.store.service.WorkQueueStats;
-import org.slf4j.Logger;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Queues;
-import com.google.common.util.concurrent.AtomicLongMap;
-
-/**
- * State machine for {@link AtomixWorkQueue} resource.
- */
-public class AtomixWorkQueueState extends ResourceStateMachine implements SessionListener, Snapshottable {
-
- private final Logger log = getLogger(getClass());
-
- private final AtomicLong totalCompleted = new AtomicLong(0);
-
- private final Queue<TaskHolder> unassignedTasks = Queues.newArrayDeque();
- private final Map<String, TaskAssignment> assignments = Maps.newHashMap();
- private final Map<Long, Commit<? extends Register>> registeredWorkers = Maps.newHashMap();
- private final AtomicLongMap<Long> activeTasksPerSession = AtomicLongMap.create();
-
- protected AtomixWorkQueueState(Properties config) {
- super(config);
- }
-
- @Override
- protected void configure(StateMachineExecutor executor) {
- executor.register(Stats.class, this::stats);
- executor.register(Register.class, (Consumer<Commit<Register>>) this::register);
- executor.register(Unregister.class, (Consumer<Commit<Unregister>>) this::unregister);
- executor.register(Add.class, (Consumer<Commit<Add>>) this::add);
- executor.register(Take.class, this::take);
- executor.register(Complete.class, (Consumer<Commit<Complete>>) this::complete);
- executor.register(Clear.class, (Consumer<Commit<Clear>>) this::clear);
- }
-
- protected WorkQueueStats stats(Commit<? extends Stats> commit) {
- try {
- return WorkQueueStats.builder()
- .withTotalCompleted(totalCompleted.get())
- .withTotalPending(unassignedTasks.size())
- .withTotalInProgress(assignments.size())
- .build();
- } finally {
- commit.close();
- }
- }
-
- protected void clear(Commit<? extends Clear> commit) {
- try {
- unassignedTasks.forEach(TaskHolder::complete);
- unassignedTasks.clear();
- assignments.values().forEach(TaskAssignment::markComplete);
- assignments.clear();
- registeredWorkers.values().forEach(Commit::close);
- registeredWorkers.clear();
- activeTasksPerSession.clear();
- totalCompleted.set(0);
- } finally {
- commit.close();
- }
- }
-
- protected void register(Commit<? extends Register> commit) {
- long sessionId = commit.session().id();
- if (registeredWorkers.putIfAbsent(sessionId, commit) != null) {
- commit.close();
- }
- }
-
- protected void unregister(Commit<? extends Unregister> commit) {
- try {
- Commit<? extends Register> registerCommit = registeredWorkers.remove(commit.session().id());
- if (registerCommit != null) {
- registerCommit.close();
- }
- } finally {
- commit.close();
- }
- }
-
- protected void add(Commit<? extends Add> commit) {
- Collection<byte[]> items = commit.operation().items();
-
- // Create a CountDownCompleter that will close the commit when all tasks
- // submitted as part of it are completed.
- CountDownCompleter<Commit<? extends Add>> referenceTracker =
- new CountDownCompleter<>(commit, items.size(), Commit::close);
-
- AtomicInteger itemIndex = new AtomicInteger(0);
- items.forEach(item -> {
- String taskId = String.format("%d:%d:%d", commit.session().id(),
- commit.index(),
- itemIndex.getAndIncrement());
- unassignedTasks.add(new TaskHolder(new Task<>(taskId, item), referenceTracker));
- });
-
- // Send an event to all sessions that have expressed interest in task processing
- // and are not actively processing a task.
- registeredWorkers.values()
- .stream()
- .map(Commit::session)
- .forEach(session -> session.publish(AtomixWorkQueue.TASK_AVAILABLE));
- // FIXME: This generates a lot of event traffic.
- }
-
- protected Collection<Task<byte[]>> take(Commit<? extends Take> commit) {
- try {
- if (unassignedTasks.isEmpty()) {
- return ImmutableList.of();
- }
- long sessionId = commit.session().id();
- int maxTasks = commit.operation().maxTasks();
- return IntStream.range(0, Math.min(maxTasks, unassignedTasks.size()))
- .mapToObj(i -> {
- TaskHolder holder = unassignedTasks.poll();
- String taskId = holder.task().taskId();
- TaskAssignment assignment = new TaskAssignment(sessionId, holder);
-
- // bookkeeping
- assignments.put(taskId, assignment);
- activeTasksPerSession.incrementAndGet(sessionId);
-
- return holder.task();
- })
- .collect(Collectors.toCollection(ArrayList::new));
- } catch (Exception e) {
- log.warn("State machine update failed", e);
- throw Throwables.propagate(e);
- } finally {
- commit.close();
- }
- }
-
- protected void complete(Commit<? extends Complete> commit) {
- long sessionId = commit.session().id();
- try {
- commit.operation().taskIds().forEach(taskId -> {
- TaskAssignment assignment = assignments.get(taskId);
- if (assignment != null && assignment.sessionId() == sessionId) {
- assignments.remove(taskId).markComplete();
- // bookkeeping
- totalCompleted.incrementAndGet();
- activeTasksPerSession.decrementAndGet(sessionId);
- }
- });
- } catch (Exception e) {
- log.warn("State machine update failed", e);
- throw Throwables.propagate(e);
- } finally {
- commit.close();
- }
- }
-
- @Override
- public void register(ServerSession session) {
- }
-
- @Override
- public void unregister(ServerSession session) {
- evictWorker(session.id());
- }
-
- @Override
- public void expire(ServerSession session) {
- evictWorker(session.id());
- }
-
- @Override
- public void close(ServerSession session) {
- evictWorker(session.id());
- }
-
- @Override
- public void snapshot(SnapshotWriter writer) {
- writer.writeLong(totalCompleted.get());
- }
-
- @Override
- public void install(SnapshotReader reader) {
- totalCompleted.set(reader.readLong());
- }
-
- private void evictWorker(long sessionId) {
- Commit<? extends Register> commit = registeredWorkers.remove(sessionId);
- if (commit != null) {
- commit.close();
- }
-
- // TODO: Maintain an index of tasks by session for efficient access.
- Iterator<Map.Entry<String, TaskAssignment>> iter = assignments.entrySet().iterator();
- while (iter.hasNext()) {
- Map.Entry<String, TaskAssignment> entry = iter.next();
- TaskAssignment assignment = entry.getValue();
- if (assignment.sessionId() == sessionId) {
- unassignedTasks.add(assignment.taskHolder());
- iter.remove();
- }
- }
-
- // Bookkeeping
- activeTasksPerSession.remove(sessionId);
- activeTasksPerSession.removeAllZeros();
- }
-
- private class TaskHolder {
-
- private final Task<byte[]> task;
- private final CountDownCompleter<Commit<? extends Add>> referenceTracker;
-
- public TaskHolder(Task<byte[]> delegate, CountDownCompleter<Commit<? extends Add>> referenceTracker) {
- this.task = delegate;
- this.referenceTracker = referenceTracker;
- }
-
- public Task<byte[]> task() {
- return task;
- }
-
- public void complete() {
- referenceTracker.countDown();
- }
- }
-
- private class TaskAssignment {
- private final long sessionId;
- private final TaskHolder taskHolder;
-
- public TaskAssignment(long sessionId, TaskHolder taskHolder) {
- this.sessionId = sessionId;
- this.taskHolder = taskHolder;
- }
-
- public long sessionId() {
- return sessionId;
- }
-
- public TaskHolder taskHolder() {
- return taskHolder;
- }
-
- public void markComplete() {
- taskHolder.complete();
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("sessionId", sessionId)
- .add("taskHolder", taskHolder)
- .toString();
- }
- }
-}
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java
index a351ad5..ba248ba 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java
@@ -41,7 +41,7 @@
public class DefaultDocumentTree<V> implements DocumentTree<V> {
private static final DocumentPath ROOT_PATH = DocumentPath.from("root");
- private final DefaultDocumentTreeNode<V> root;
+ final DefaultDocumentTreeNode<V> root;
private final Supplier<Long> versionSupplier;
public DefaultDocumentTree() {
@@ -55,6 +55,11 @@
this.versionSupplier = versionSupplier;
}
+ DefaultDocumentTree(Supplier<Long> versionSupplier, DefaultDocumentTreeNode<V> root) {
+ this.root = root;
+ this.versionSupplier = versionSupplier;
+ }
+
@Override
public DocumentPath root() {
return ROOT_PATH;
@@ -195,4 +200,4 @@
throw new IllegalDocumentModificationException();
}
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java
index 4b4852b..6568779 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java
@@ -41,9 +41,9 @@
private final DocumentTreeNode<V> parent;
public DefaultDocumentTreeNode(DocumentPath key,
- V value,
- long version,
- DocumentTreeNode<V> parent) {
+ V value,
+ long version,
+ DocumentTreeNode<V> parent) {
this.key = checkNotNull(key);
this.value = new Versioned<>(value, version);
this.parent = parent;
@@ -137,9 +137,9 @@
public String toString() {
MoreObjects.ToStringHelper helper =
MoreObjects.toStringHelper(getClass())
- .add("parent", this.parent)
- .add("key", this.key)
- .add("value", this.value);
+ .add("parent", this.parent)
+ .add("key", this.key)
+ .add("value", this.value);
for (DocumentTreeNode<V> child : children.values()) {
helper = helper.add("child", "\n" + child.path().pathElements()
.get(child.path().pathElements().size() - 1) +
@@ -147,4 +147,4 @@
}
return helper.toString();
}
-}
+}
\ No newline at end of file
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java
deleted file mode 100644
index d62f4af..0000000
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.onosproject.store.primitives.impl;
-
-import java.time.Duration;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-
-import com.google.common.collect.Lists;
-import io.atomix.catalyst.concurrent.SingleThreadContext;
-import io.atomix.catalyst.concurrent.ThreadContext;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.Client;
-import io.atomix.catalyst.transport.Server;
-import io.atomix.catalyst.transport.Transport;
-import io.atomix.copycat.protocol.ConnectRequest;
-import io.atomix.copycat.protocol.ConnectResponse;
-import io.atomix.copycat.protocol.Response;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.IpAddress;
-import org.onlab.util.Tools;
-import org.onosproject.cluster.PartitionId;
-import org.onosproject.store.cluster.messaging.Endpoint;
-import org.onosproject.store.cluster.messaging.MessagingException;
-import org.onosproject.store.cluster.messaging.MessagingService;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.onlab.junit.TestTools.findAvailablePort;
-
-/**
- * Copycat transport test.
- */
-public class CopycatTransportTest {
-
- private static final String IP_STRING = "127.0.0.1";
-
- private Endpoint endpoint1 = new Endpoint(IpAddress.valueOf(IP_STRING), 5001);
- private Endpoint endpoint2 = new Endpoint(IpAddress.valueOf(IP_STRING), 5002);
-
- private TestMessagingService clientService;
- private TestMessagingService serverService;
-
- private Transport clientTransport;
- private ThreadContext clientContext;
-
- private Transport serverTransport;
- private ThreadContext serverContext;
-
- @Before
- public void setUp() throws Exception {
- Map<Endpoint, TestMessagingService> services = new ConcurrentHashMap<>();
-
- endpoint1 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5001));
- clientService = new TestMessagingService(endpoint1, services);
- clientTransport = new CopycatTransport(PartitionId.from(1), clientService);
- clientContext = new SingleThreadContext("client-test-%d", CatalystSerializers.getSerializer());
-
- endpoint2 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5003));
- serverService = new TestMessagingService(endpoint2, services);
- serverTransport = new CopycatTransport(PartitionId.from(1), serverService);
- serverContext = new SingleThreadContext("server-test-%d", CatalystSerializers.getSerializer());
- }
-
- @After
- public void tearDown() throws Exception {
- if (clientContext != null) {
- clientContext.close();
- }
- if (serverContext != null) {
- serverContext.close();
- }
- }
-
- /**
- * Tests sending a message from the client side of a Copycat connection to the server side.
- */
- @Test
- public void testCopycatClientConnectionSend() throws Exception {
- Client client = clientTransport.client();
- Server server = serverTransport.server();
-
- CountDownLatch latch = new CountDownLatch(4);
- CountDownLatch listenLatch = new CountDownLatch(1);
- CountDownLatch handlerLatch = new CountDownLatch(1);
- serverContext.executor().execute(() -> {
- server.listen(new Address(IP_STRING, endpoint2.port()), connection -> {
- serverContext.checkThread();
- latch.countDown();
- connection.handler(ConnectRequest.class, request -> {
- serverContext.checkThread();
- latch.countDown();
- return CompletableFuture.completedFuture(ConnectResponse.builder()
- .withStatus(Response.Status.OK)
- .withLeader(new Address(IP_STRING, endpoint2.port()))
- .withMembers(Lists.newArrayList(new Address(IP_STRING, endpoint2.port())))
- .build());
- });
- handlerLatch.countDown();
- }).thenRun(listenLatch::countDown);
- });
-
- listenLatch.await(5, TimeUnit.SECONDS);
-
- clientContext.executor().execute(() -> {
- client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> {
- clientContext.checkThread();
- latch.countDown();
- try {
- handlerLatch.await(5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- fail();
- }
- connection.<ConnectRequest, ConnectResponse>sendAndReceive(ConnectRequest.builder()
- .withClientId(UUID.randomUUID().toString())
- .build())
- .thenAccept(response -> {
- clientContext.checkThread();
- assertNotNull(response);
- assertEquals(Response.Status.OK, response.status());
- latch.countDown();
- });
- });
- });
-
- latch.await(5, TimeUnit.SECONDS);
- assertEquals(0, latch.getCount());
- }
-
- /**
- * Tests sending a message from the server side of a Copycat connection to the client side.
- */
- @Test
- public void testCopycatServerConnectionSend() throws Exception {
- Client client = clientTransport.client();
- Server server = serverTransport.server();
-
- CountDownLatch latch = new CountDownLatch(4);
- CountDownLatch listenLatch = new CountDownLatch(1);
- serverContext.executor().execute(() -> {
- server.listen(new Address(IP_STRING, endpoint2.port()), connection -> {
- serverContext.checkThread();
- latch.countDown();
- serverContext.schedule(Duration.ofMillis(100), () -> {
- connection.<ConnectRequest, ConnectResponse>sendAndReceive(ConnectRequest.builder()
- .withClientId("foo")
- .build())
- .thenAccept(response -> {
- serverContext.checkThread();
- assertEquals(Response.Status.OK, response.status());
- latch.countDown();
- });
- });
- }).thenRun(listenLatch::countDown);
- });
-
- listenLatch.await(5, TimeUnit.SECONDS);
-
- clientContext.executor().execute(() -> {
- client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> {
- clientContext.checkThread();
- latch.countDown();
- connection.handler(ConnectRequest.class, request -> {
- clientContext.checkThread();
- latch.countDown();
- assertEquals("foo", request.client());
- return CompletableFuture.completedFuture(ConnectResponse.builder()
- .withStatus(Response.Status.OK)
- .withLeader(new Address(IP_STRING, endpoint2.port()))
- .withMembers(Lists.newArrayList(new Address(IP_STRING, endpoint2.port())))
- .build());
- });
- });
- });
-
- latch.await(5, TimeUnit.SECONDS);
- assertEquals(0, latch.getCount());
- }
-
- /**
- * Tests closing the server side of a Copycat connection.
- */
- @Test
- public void testCopycatClientConnectionClose() throws Exception {
- Client client = clientTransport.client();
- Server server = serverTransport.server();
-
- CountDownLatch latch = new CountDownLatch(5);
- CountDownLatch listenLatch = new CountDownLatch(1);
- serverContext.executor().execute(() -> {
- server.listen(new Address(IP_STRING, endpoint2.port()), connection -> {
- serverContext.checkThread();
- latch.countDown();
- connection.onClose(c -> {
- serverContext.checkThread();
- latch.countDown();
- });
- }).thenRun(listenLatch::countDown);
- });
-
- listenLatch.await(5, TimeUnit.SECONDS);
-
- clientContext.executor().execute(() -> {
- client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> {
- clientContext.checkThread();
- latch.countDown();
- connection.onClose(c -> {
- clientContext.checkThread();
- latch.countDown();
- });
- clientContext.schedule(Duration.ofMillis(100), () -> {
- connection.close().whenComplete((result, error) -> {
- clientContext.checkThread();
- latch.countDown();
- });
- });
- });
- });
-
- latch.await(5, TimeUnit.SECONDS);
- assertEquals(0, latch.getCount());
- }
-
- /**
- * Tests that a client connection is closed on exception.
- */
- @Test
- public void testCopycatClientConnectionCloseOnException() throws Exception {
- Client client = clientTransport.client();
- Server server = serverTransport.server();
-
- CountDownLatch listenLatch = new CountDownLatch(1);
- CountDownLatch closeLatch = new CountDownLatch(1);
- CountDownLatch latch = new CountDownLatch(1);
- serverContext.executor().execute(() -> {
- server.listen(new Address(IP_STRING, endpoint2.port()), connection -> {
- serverContext.checkThread();
- }).thenRun(listenLatch::countDown);
- });
-
- listenLatch.await(5, TimeUnit.SECONDS);
-
- clientContext.executor().execute(() -> {
- client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> {
- clientContext.checkThread();
- serverService.handlers.clear();
- connection.onClose(c -> latch.countDown());
- connection.<ConnectRequest, ConnectResponse>sendAndReceive(ConnectRequest.builder()
- .withClientId(UUID.randomUUID().toString())
- .build())
- .thenAccept(response -> fail());
- });
- });
-
- latch.await(5, TimeUnit.SECONDS);
- assertEquals(0, latch.getCount());
- }
-
- /**
- * Tests closing the server side of a Copycat connection.
- */
- @Test
- public void testCopycatServerConnectionClose() throws Exception {
- Client client = clientTransport.client();
- Server server = serverTransport.server();
-
- CountDownLatch latch = new CountDownLatch(5);
- CountDownLatch listenLatch = new CountDownLatch(1);
- serverContext.executor().execute(() -> {
- server.listen(new Address(IP_STRING, endpoint2.port()), connection -> {
- serverContext.checkThread();
- latch.countDown();
- connection.onClose(c -> {
- latch.countDown();
- });
- serverContext.schedule(Duration.ofMillis(100), () -> {
- connection.close().whenComplete((result, error) -> {
- serverContext.checkThread();
- latch.countDown();
- });
- });
- }).thenRun(listenLatch::countDown);
- });
-
- listenLatch.await(5, TimeUnit.SECONDS);
-
- clientContext.executor().execute(() -> {
- client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> {
- clientContext.checkThread();
- latch.countDown();
- connection.onClose(c -> {
- latch.countDown();
- });
- });
- });
-
- latch.await(5, TimeUnit.SECONDS);
- assertEquals(0, latch.getCount());
- }
-
- /**
- * Tests that a server connection is closed on exception.
- */
- @Test
- public void testCopycatServerConnectionCloseOnException() throws Exception {
- Client client = clientTransport.client();
- Server server = serverTransport.server();
-
- CountDownLatch latch = new CountDownLatch(1);
- CountDownLatch listenLatch = new CountDownLatch(1);
- CountDownLatch connectLatch = new CountDownLatch(1);
- serverContext.executor().execute(() -> {
- server.listen(new Address(IP_STRING, endpoint2.port()), connection -> {
- serverContext.checkThread();
- serverContext.executor().execute(() -> {
- try {
- connectLatch.await(5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- fail();
- }
- clientService.handlers.clear();
- connection.onClose(c -> latch.countDown());
- connection.<ConnectRequest, ConnectResponse>sendAndReceive(ConnectRequest.builder()
- .withClientId("foo")
- .build())
- .thenAccept(response -> fail());
- });
- }).thenRun(listenLatch::countDown);
- });
-
- listenLatch.await(5, TimeUnit.SECONDS);
-
- clientContext.executor().execute(() -> {
- client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> {
- clientContext.checkThread();
- connectLatch.countDown();
- });
- });
-
- latch.await(5, TimeUnit.SECONDS);
- assertEquals(0, latch.getCount());
- }
-
- /**
- * Custom implementation of {@code MessagingService} used for testing. Really, this should
- * be mocked but suffices for now.
- */
- public static final class TestMessagingService implements MessagingService {
- private final Endpoint endpoint;
- private final Map<Endpoint, TestMessagingService> services;
- private final Map<String, BiFunction<Endpoint, byte[], CompletableFuture<byte[]>>> handlers =
- new ConcurrentHashMap<>();
-
- TestMessagingService(Endpoint endpoint, Map<Endpoint, TestMessagingService> services) {
- this.endpoint = endpoint;
- this.services = services;
- services.put(endpoint, this);
- }
-
- private CompletableFuture<byte[]> handle(Endpoint ep, String type, byte[] message, Executor executor) {
- BiFunction<Endpoint, byte[], CompletableFuture<byte[]>> handler = handlers.get(type);
- if (handler == null) {
- return Tools.exceptionalFuture(new MessagingException.NoRemoteHandler());
- }
- return handler.apply(ep, message).thenApplyAsync(r -> r, executor);
- }
-
- @Override
- public CompletableFuture<Void> sendAsync(Endpoint ep, String type, byte[] payload) {
- // Unused for testing
- return null;
- }
-
- @Override
- public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload) {
- // Unused for testing
- return null;
- }
-
- @Override
- public CompletableFuture<byte[]> sendAndReceive(Endpoint ep, String type, byte[] payload, Executor executor) {
- TestMessagingService service = services.get(ep);
- if (service == null) {
- return Tools.exceptionalFuture(new IllegalStateException());
- }
- return service.handle(endpoint, type, payload, executor);
- }
-
- @Override
- public void registerHandler(String type, BiConsumer<Endpoint, byte[]> handler, Executor executor) {
- // Unused for testing
- }
-
- @Override
- public void registerHandler(String type, BiFunction<Endpoint, byte[], byte[]> handler, Executor executor) {
- // Unused for testing
- }
-
- @Override
- public void registerHandler(String type, BiFunction<Endpoint, byte[], CompletableFuture<byte[]>> handler) {
- handlers.put(type, handler);
- }
-
- @Override
- public void unregisterHandler(String type) {
- handlers.remove(type);
- }
- }
-
-}
\ No newline at end of file
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java
new file mode 100644
index 0000000..6d7007a
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT;
+
+/**
+ * Atomic counter map service test.
+ */
+public class AtomixAtomicCounterMapServiceTest {
+ @Test
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ AtomixAtomicCounterMapService service = new AtomixAtomicCounterMapService();
+ service.put(new DefaultCommit<>(
+ 2,
+ PUT,
+ new AtomixAtomicCounterMapOperations.Put("foo", 1),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixAtomicCounterMapService();
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ long value = service.get(new DefaultCommit<>(
+ 2,
+ GET,
+ new AtomixAtomicCounterMapOperations.Get("foo"),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertEquals(1, value);
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java
index 63db592..ee44b29 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java
@@ -15,9 +15,8 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.resource.ResourceType;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@@ -26,21 +25,16 @@
/**
* Unit test for {@code AtomixCounterMap}.
*/
-public class AtomixAtomicCounterMapTest extends AtomixTestBase {
+public class AtomixAtomicCounterMapTest extends AtomixTestBase<AtomixAtomicCounterMap> {
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
+ @Override
+ protected RaftService createService() {
+ return new AtomixAtomicCounterMapService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixAtomicCounterMap.class);
+ protected AtomixAtomicCounterMap createPrimitive(RaftProxy proxy) {
+ return new AtomixAtomicCounterMap(proxy);
}
/**
@@ -48,8 +42,7 @@
*/
@Test
public void testBasicCounterMapOperations() throws Throwable {
- AtomixAtomicCounterMap map = createAtomixClient().getResource("testBasicCounterMapOperationMap",
- AtomixAtomicCounterMap.class).join();
+ AtomixAtomicCounterMap map = newPrimitive("testBasicCounterMapOperationMap");
map.isEmpty().thenAccept(isEmpty -> {
assertTrue(isEmpty);
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java
new file mode 100644
index 0000000..098c193
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import org.junit.Test;
+import org.onlab.util.Match;
+import org.onosproject.store.service.Versioned;
+
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UPDATE_AND_GET;
+
+/**
+ * Consistent map service test.
+ */
+public class AtomixConsistentMapServiceTest {
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ AtomixConsistentMapService service = new AtomixConsistentMapService();
+ service.updateAndGet(new DefaultCommit<>(
+ 2,
+ UPDATE_AND_GET,
+ new AtomixConsistentMapOperations.UpdateAndGet("foo", "Hello world!".getBytes(), Match.ANY, Match.ANY),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixConsistentMapService();
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ Versioned<byte[]> value = service.get(new DefaultCommit<>(
+ 2,
+ GET,
+ new AtomixConsistentMapOperations.Get("foo"),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertNotNull(value);
+ assertArrayEquals("Hello world!".getBytes(), value.value());
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java
index 284b57b..e858b3f 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java
@@ -15,11 +15,18 @@
*/
package org.onosproject.store.primitives.resources.impl;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletionException;
+import java.util.stream.Collectors;
+
import com.google.common.base.Throwables;
import com.google.common.collect.Sets;
-import io.atomix.resource.ResourceType;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test;
import org.onlab.util.Tools;
import org.onosproject.store.primitives.MapUpdate;
@@ -30,14 +37,6 @@
import org.onosproject.store.service.Version;
import org.onosproject.store.service.Versioned;
-import java.util.Arrays;
-import java.util.ConcurrentModificationException;
-import java.util.List;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletionException;
-import java.util.stream.Collectors;
-
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -51,20 +50,16 @@
/**
* Unit tests for {@link AtomixConsistentMap}.
*/
-public class AtomixConsistentMapTest extends AtomixTestBase {
+public class AtomixConsistentMapTest extends AtomixTestBase<AtomixConsistentMap> {
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
- }
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixConsistentMap.class);
+ protected RaftService createService() {
+ return new AtomixConsistentMapService();
+ }
+
+ @Override
+ protected AtomixConsistentMap createPrimitive(RaftProxy proxy) {
+ return new AtomixConsistentMap(proxy);
}
/**
@@ -119,8 +114,7 @@
final byte[] rawFooValue = Tools.getBytesUtf8("Hello foo!");
final byte[] rawBarValue = Tools.getBytesUtf8("Hello bar!");
- AtomixConsistentMap map = createAtomixClient().getResource("testBasicMapOperationMap",
- AtomixConsistentMap.class).join();
+ AtomixConsistentMap map = newPrimitive("testBasicMapOperationMap");
map.isEmpty().thenAccept(result -> {
assertTrue(result);
@@ -249,8 +243,7 @@
final byte[] value2 = Tools.getBytesUtf8("value2");
final byte[] value3 = Tools.getBytesUtf8("value3");
- AtomixConsistentMap map = createAtomixClient().getResource("testMapComputeOperationsMap",
- AtomixConsistentMap.class).join();
+ AtomixConsistentMap map = newPrimitive("testMapComputeOperationsMap");
map.computeIfAbsent("foo", k -> value1).thenAccept(result -> {
assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1));
@@ -287,8 +280,7 @@
final byte[] value2 = Tools.getBytesUtf8("value2");
final byte[] value3 = Tools.getBytesUtf8("value3");
- AtomixConsistentMap map = createAtomixClient().getResource("testMapListenerMap",
- AtomixConsistentMap.class).join();
+ AtomixConsistentMap map = newPrimitive("testMapListenerMap");
TestMapEventListener listener = new TestMapEventListener();
// add listener; insert new value into map and verify an INSERT event is received.
@@ -343,8 +335,7 @@
}
protected void transactionPrepareTests() throws Throwable {
- AtomixConsistentMap map = createAtomixClient().getResource("testPrepareTestsMap",
- AtomixConsistentMap.class).join();
+ AtomixConsistentMap map = newPrimitive("testPrepareTestsMap");
TransactionId transactionId1 = TransactionId.from("tx1");
TransactionId transactionId2 = TransactionId.from("tx2");
@@ -420,8 +411,7 @@
final byte[] value1 = Tools.getBytesUtf8("value1");
final byte[] value2 = Tools.getBytesUtf8("value2");
- AtomixConsistentMap map = createAtomixClient().getResource("testCommitTestsMap",
- AtomixConsistentMap.class).join();
+ AtomixConsistentMap map = newPrimitive("testCommitTestsMap");
TestMapEventListener listener = new TestMapEventListener();
map.addListener(listener).join();
@@ -521,8 +511,7 @@
final byte[] value1 = Tools.getBytesUtf8("value1");
final byte[] value2 = Tools.getBytesUtf8("value2");
- AtomixConsistentMap map = createAtomixClient().getResource("testTransactionRollbackTestsMap",
- AtomixConsistentMap.class).join();
+ AtomixConsistentMap map = newPrimitive("testTransactionRollbackTestsMap");
TestMapEventListener listener = new TestMapEventListener();
map.addListener(listener).join();
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java
new file mode 100644
index 0000000..836c08c
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import org.junit.Test;
+import org.onlab.util.Match;
+import org.onosproject.store.service.Versioned;
+
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.PUT;
+
+/**
+ * Consistent set multimap service test.
+ */
+public class AtomixConsistentSetMultimapServiceTest {
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ AtomixConsistentSetMultimapService service = new AtomixConsistentSetMultimapService();
+ service.put(new DefaultCommit<>(
+ 2,
+ PUT,
+ new AtomixConsistentSetMultimapOperations.Put(
+ "foo", Arrays.asList("Hello world!".getBytes()), Match.ANY),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixConsistentSetMultimapService();
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ Versioned<Collection<? extends byte[]>> value = service.get(new DefaultCommit<>(
+ 2,
+ GET,
+ new AtomixConsistentSetMultimapOperations.Get("foo"),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertNotNull(value);
+ assertEquals(1, value.value().size());
+ assertArrayEquals("Hello world!".getBytes(), value.value().iterator().next());
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java
index b1ec1f8..2d01912 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java
@@ -16,22 +16,21 @@
package org.onosproject.store.primitives.resources.impl;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multiset;
-import com.google.common.collect.TreeMultiset;
-import io.atomix.resource.ResourceType;
-import org.apache.commons.collections.keyvalue.DefaultMapEntry;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.onlab.util.Tools;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.TreeMultiset;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
+import org.apache.commons.collections.keyvalue.DefaultMapEntry;
+import org.junit.Test;
+import org.onlab.util.Tools;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -39,7 +38,7 @@
/**
* Tests the {@link AtomixConsistentSetMultimap}.
*/
-public class AtomixConsistentSetMultimapTest extends AtomixTestBase {
+public class AtomixConsistentSetMultimapTest extends AtomixTestBase<AtomixConsistentSetMultimap> {
private final String keyOne = "hello";
private final String keyTwo = "goodbye";
private final String keyThree = "foo";
@@ -55,19 +54,14 @@
valueThree,
valueFour);
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
+ @Override
+ protected RaftService createService() {
+ return new AtomixConsistentSetMultimapService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixConsistentSetMultimap.class);
+ protected AtomixConsistentSetMultimap createPrimitive(RaftProxy proxy) {
+ return new AtomixConsistentSetMultimap(proxy);
}
/**
@@ -154,9 +148,10 @@
});
});
+ final String[] removedKey = new String[1];
+
//Test behavior after removals
allValues.forEach(value -> {
- final String[] removedKey = new String[1];
allKeys.forEach(key -> {
map.remove(key, value)
.thenAccept(result -> assertTrue(result)).join();
@@ -164,11 +159,12 @@
.thenAccept(result -> assertFalse(result)).join();
removedKey[0] = key;
});
- //Check that contains key works properly for removed keys
- map.containsKey(removedKey[0])
- .thenAccept(result -> assertFalse(result));
});
+ //Check that contains key works properly for removed keys
+ map.containsKey(removedKey[0])
+ .thenAccept(result -> assertFalse(result));
+
//Check that contains value works correctly for removed values
allValues.forEach(value -> {
map.containsValue(value)
@@ -403,9 +399,7 @@
private AtomixConsistentSetMultimap createResource(String mapName) {
try {
- AtomixConsistentSetMultimap map = createAtomixClient().
- getResource(mapName, AtomixConsistentSetMultimap.class)
- .join();
+ AtomixConsistentSetMultimap map = newPrimitive(mapName);
return map;
} catch (Throwable e) {
throw new RuntimeException(e.toString());
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java
new file mode 100644
index 0000000..04698e0
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import org.junit.Test;
+import org.onlab.util.Match;
+import org.onosproject.store.service.Versioned;
+
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UPDATE_AND_GET;
+
+/**
+ * Consistent tree map service test.
+ */
+public class AtomixConsistentTreeMapServiceTest {
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ AtomixConsistentTreeMapService service = new AtomixConsistentTreeMapService();
+ service.updateAndGet(new DefaultCommit<>(
+ 2,
+ UPDATE_AND_GET,
+ new AtomixConsistentTreeMapOperations.UpdateAndGet(
+ "foo", "Hello world!".getBytes(), Match.ANY, Match.ANY),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixConsistentTreeMapService();
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ Versioned<byte[]> value = service.get(new DefaultCommit<>(
+ 2,
+ GET,
+ new AtomixConsistentTreeMapOperations.Get("foo"),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertNotNull(value);
+ assertArrayEquals("Hello world!".getBytes(), value.value());
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java
index bbaaf57..f1de625 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java
@@ -15,16 +15,6 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import io.atomix.resource.ResourceType;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.onlab.util.Tools;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.MapEventListener;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -33,6 +23,15 @@
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
+import org.junit.Test;
+import org.onlab.util.Tools;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,7 +43,7 @@
/**
* Unit tests for {@link AtomixConsistentTreeMap}.
*/
-public class AtomixConsistentTreeMapTest extends AtomixTestBase {
+public class AtomixConsistentTreeMapTest extends AtomixTestBase<AtomixConsistentTreeMap> {
private final String keyFour = "hello";
private final String keyThree = "goodbye";
private final String keyTwo = "foo";
@@ -60,19 +59,15 @@
valueTwo,
valueThree,
valueFour);
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
- @AfterClass
- public static void postTestCleanup() throws Throwable {
- clearTests();
+ @Override
+ protected RaftService createService() {
+ return new AtomixConsistentTreeMapService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixConsistentTreeMap.class);
+ protected AtomixConsistentTreeMap createPrimitive(RaftProxy proxy) {
+ return new AtomixConsistentTreeMap(proxy);
}
/**
@@ -359,7 +354,9 @@
map.ceilingKey(keyOne).thenAccept(result -> assertNull(result))
.join();
map.higherKey(keyOne).thenAccept(result -> assertNull(result)).join();
- map.delete().join();
+
+ // TODO: delete() is not supported
+ //map.delete().join();
allKeys.forEach(key -> map.put(
key, allValues.get(allKeys.indexOf(key)))
@@ -481,15 +478,14 @@
map.higherKey(keyFour).thenAccept(
result -> assertNull(result))
.join();
- map.delete().join();
+ // TODO: delete() is not supported
+ //map.delete().join();
}
private AtomixConsistentTreeMap createResource(String mapName) {
try {
- AtomixConsistentTreeMap map = createAtomixClient().
- getResource(mapName, AtomixConsistentTreeMap.class)
- .join();
+ AtomixConsistentTreeMap map = newPrimitive(mapName);
return map;
} catch (Throwable e) {
throw new RuntimeException(e.toString());
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java
new file mode 100644
index 0000000..707aa91
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.SET;
+
+/**
+ * Counter service test.
+ */
+public class AtomixCounterServiceTest {
+ @Test
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ AtomixCounterService service = new AtomixCounterService();
+ service.set(new DefaultCommit<>(
+ 2,
+ SET,
+ new AtomixCounterOperations.Set(1L),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixCounterService();
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ long value = service.get(new DefaultCommit<>(
+ 2,
+ GET,
+ null,
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertEquals(1, value);
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLongTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterTest.java
similarity index 69%
rename from core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLongTest.java
rename to core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterTest.java
index eb835cd..2e334d6 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLongTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterTest.java
@@ -15,35 +15,26 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.Atomix;
-import io.atomix.resource.ResourceType;
-import io.atomix.variables.DistributedLong;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-/**git s
+/**
* Unit tests for {@link AtomixCounter}.
*/
-public class AtomixLongTest extends AtomixTestBase {
-
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
+public class AtomixCounterTest extends AtomixTestBase<AtomixCounter> {
+ @Override
+ protected RaftService createService() {
+ return new AtomixCounterService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(DistributedLong.class);
+ protected AtomixCounter createPrimitive(RaftProxy proxy) {
+ return new AtomixCounter(proxy);
}
@Test
@@ -52,9 +43,7 @@
}
protected void basicOperationsTest() throws Throwable {
- Atomix atomix = createAtomixClient();
- AtomixCounter along = new AtomixCounter("test-long-basic-operations",
- atomix.getLong("test-long").join());
+ AtomixCounter along = newPrimitive("test-counter-basic-operations");
assertEquals(0, along.get().join().longValue());
assertEquals(1, along.incrementAndGet().join().longValue());
along.set(100).join();
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java
new file mode 100644
index 0000000..8e1ae91
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Optional;
+
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import org.junit.Test;
+import org.onlab.util.Match;
+import org.onosproject.store.service.DocumentPath;
+import org.onosproject.store.service.Versioned;
+
+import static org.easymock.EasyMock.mock;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET;
+import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.UPDATE;
+
+/**
+ * Document tree service test.
+ */
+public class AtomixDocumentTreeServiceTest {
+ @Test
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ AtomixDocumentTreeService service = new AtomixDocumentTreeService();
+ service.update(new DefaultCommit<>(
+ 2,
+ UPDATE,
+ new AtomixDocumentTreeOperations.Update(
+ DocumentPath.from("root|foo"),
+ Optional.of("Hello world!".getBytes()),
+ Match.any(),
+ Match.ifNull()),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixDocumentTreeService();
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ Versioned<byte[]> value = service.get(new DefaultCommit<>(
+ 2,
+ GET,
+ new AtomixDocumentTreeOperations.Get(DocumentPath.from("root|foo")),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertNotNull(value);
+ assertArrayEquals("Hello world!".getBytes(), value.value());
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java
index dbf8c66..ec81c41 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java
@@ -16,22 +16,14 @@
package org.onosproject.store.primitives.resources.impl;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import io.atomix.AtomixClient;
-import io.atomix.resource.ResourceType;
-
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import com.google.common.base.Throwables;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test;
import org.onosproject.store.service.DocumentPath;
import org.onosproject.store.service.DocumentTreeEvent;
@@ -40,32 +32,34 @@
import org.onosproject.store.service.NoSuchDocumentPathException;
import org.onosproject.store.service.Versioned;
-import com.google.common.base.Throwables;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Unit tests for {@link AtomixDocumentTree}.
*/
-public class AtomixDocumentTreeTest extends AtomixTestBase {
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
+public class AtomixDocumentTreeTest extends AtomixTestBase<AtomixDocumentTree> {
+
+ @Override
+ protected RaftService createService() {
+ return new AtomixDocumentTreeService();
}
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
- }
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixDocumentTree.class);
+ protected AtomixDocumentTree createPrimitive(RaftProxy proxy) {
+ return new AtomixDocumentTree(proxy);
}
+
/**
* Tests queries (get and getChildren).
*/
@Test
public void testQueries() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
Versioned<byte[]> root = tree.get(path("root")).join();
assertEquals(1, root.version());
assertNull(root.value());
@@ -76,8 +70,7 @@
*/
@Test
public void testCreate() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -100,8 +93,7 @@
*/
@Test
public void testRecursiveCreate() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.createRecursive(path("root.a.b.c"), "abc".getBytes()).join();
Versioned<byte[]> a = tree.get(path("root.a")).join();
assertArrayEquals(null, a.value());
@@ -118,8 +110,7 @@
*/
@Test
public void testSet() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -146,8 +137,7 @@
*/
@Test
public void testReplaceVersion() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -168,8 +158,7 @@
*/
@Test
public void testReplaceValue() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -190,8 +179,7 @@
*/
@Test
public void testRemove() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -219,8 +207,7 @@
*/
@Test
public void testRemoveFailures() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -252,8 +239,7 @@
*/
@Test
public void testCreateFailures() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
try {
tree.create(path("root.a.c"), "ac".getBytes()).join();
fail();
@@ -267,8 +253,7 @@
*/
@Test
public void testSetFailures() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
try {
tree.set(path("root.a.c"), "ac".getBytes()).join();
fail();
@@ -282,8 +267,7 @@
*/
@Test
public void testGetChildren() throws Throwable {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -309,8 +293,7 @@
*/
@Test
public void testClear() {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
tree.create(path("root.a"), "a".getBytes()).join();
tree.create(path("root.a.b"), "ab".getBytes()).join();
tree.create(path("root.a.c"), "ac".getBytes()).join();
@@ -324,8 +307,7 @@
*/
@Test
public void testNotifications() throws Exception {
- AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(),
- AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString());
TestEventListener listener = new TestEventListener();
// add listener; create a node in the tree and verify an CREATED event is received.
@@ -359,12 +341,9 @@
@Test
public void testFilteredNotifications() throws Throwable {
- AtomixClient client1 = createAtomixClient();
- AtomixClient client2 = createAtomixClient();
-
String treeName = UUID.randomUUID().toString();
- AtomixDocumentTree tree1 = client1.getResource(treeName, AtomixDocumentTree.class).join();
- AtomixDocumentTree tree2 = client2.getResource(treeName, AtomixDocumentTree.class).join();
+ AtomixDocumentTree tree1 = newPrimitive(treeName);
+ AtomixDocumentTree tree2 = newPrimitive(treeName);
TestEventListener listener1a = new TestEventListener(3);
TestEventListener listener1ab = new TestEventListener(2);
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java
index 1fbc464..2a9ca02 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java
@@ -17,10 +17,8 @@
import java.util.concurrent.CompletableFuture;
-import io.atomix.resource.ResourceType;
-import io.atomix.variables.DistributedLong;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@@ -28,21 +26,16 @@
/**
* Unit test for {@code AtomixIdGenerator}.
*/
-public class AtomixIdGeneratorTest extends AtomixTestBase {
+public class AtomixIdGeneratorTest extends AtomixTestBase<AtomixCounter> {
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
+ @Override
+ protected RaftService createService() {
+ return new AtomixCounterService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(DistributedLong.class);
+ protected AtomixCounter createPrimitive(RaftProxy proxy) {
+ return new AtomixCounter(proxy);
}
/**
@@ -50,10 +43,8 @@
*/
@Test
public void testNextId() throws Throwable {
- AtomixIdGenerator idGenerator1 = new AtomixIdGenerator("testNextId",
- createAtomixClient().getLong("testNextId").join());
- AtomixIdGenerator idGenerator2 = new AtomixIdGenerator("testNextId",
- createAtomixClient().getLong("testNextId").join());
+ AtomixIdGenerator idGenerator1 = new AtomixIdGenerator(newPrimitive("testNextId"));
+ AtomixIdGenerator idGenerator2 = new AtomixIdGenerator(newPrimitive("testNextId"));
CompletableFuture<Long> future11 = idGenerator1.nextId();
CompletableFuture<Long> future12 = idGenerator1.nextId();
@@ -82,10 +73,8 @@
*/
@Test
public void testNextIdBatchRollover() throws Throwable {
- AtomixIdGenerator idGenerator1 = new AtomixIdGenerator("testNextIdBatchRollover",
- createAtomixClient().getLong("testNextIdBatchRollover").join(), 2);
- AtomixIdGenerator idGenerator2 = new AtomixIdGenerator("testNextIdBatchRollover",
- createAtomixClient().getLong("testNextIdBatchRollover").join(), 2);
+ AtomixIdGenerator idGenerator1 = new AtomixIdGenerator(newPrimitive("testNextIdBatchRollover"), 2);
+ AtomixIdGenerator idGenerator2 = new AtomixIdGenerator(newPrimitive("testNextIdBatchRollover"), 2);
CompletableFuture<Long> future11 = idGenerator1.nextId();
CompletableFuture<Long> future12 = idGenerator1.nextId();
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java
new file mode 100644
index 0000000..d3b6343
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import io.atomix.protocols.raft.ReadConsistency;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.impl.RaftServerContext;
+import io.atomix.protocols.raft.protocol.RaftServerProtocol;
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.ServiceType;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.service.impl.DefaultServiceContext;
+import io.atomix.protocols.raft.session.SessionId;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import io.atomix.utils.concurrent.ThreadContext;
+import org.junit.Test;
+import org.onosproject.cluster.Leadership;
+import org.onosproject.cluster.NodeId;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.mock;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_LEADERSHIP;
+import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.RUN;
+import static org.onosproject.store.service.DistributedPrimitive.Type.LEADER_ELECTOR;
+
+/**
+ * Leader elector service test.
+ */
+public class AtomixLeaderElectorServiceTest {
+ @Test
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ DefaultServiceContext context = mock(DefaultServiceContext.class);
+ expect(context.serviceType()).andReturn(ServiceType.from(LEADER_ELECTOR.name())).anyTimes();
+ expect(context.serviceName()).andReturn("test").anyTimes();
+ expect(context.serviceId()).andReturn(ServiceId.from(1)).anyTimes();
+ expect(context.executor()).andReturn(mock(ThreadContext.class)).anyTimes();
+
+ RaftServerContext server = mock(RaftServerContext.class);
+ expect(server.getProtocol()).andReturn(mock(RaftServerProtocol.class));
+
+ replay(context, server);
+
+ AtomixLeaderElectorService service = new AtomixLeaderElectorService();
+ service.init(context);
+
+ NodeId nodeId = NodeId.nodeId("1");
+ service.run(new DefaultCommit<>(
+ 2,
+ RUN,
+ new AtomixLeaderElectorOperations.Run("test", nodeId),
+ new RaftSessionContext(
+ SessionId.from(1),
+ MemberId.from("1"),
+ "test",
+ ServiceType.from(LEADER_ELECTOR.name()),
+ ReadConsistency.LINEARIZABLE,
+ 5000,
+ context,
+ server),
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixLeaderElectorService();
+ service.init(context);
+
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ Leadership value = service.getLeadership(new DefaultCommit<>(
+ 2,
+ GET_LEADERSHIP,
+ new AtomixLeaderElectorOperations.GetLeadership("test"),
+ mock(RaftSessionContext.class),
+ System.currentTimeMillis()));
+ assertNotNull(value);
+ assertEquals(value.leader().nodeId(), nodeId);
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java
index 00df1fc..4bd6d7f 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java
@@ -15,21 +15,18 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import io.atomix.Atomix;
-import io.atomix.AtomixClient;
-import io.atomix.resource.ResourceType;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.onosproject.cluster.Leadership;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.event.Change;
-
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
+import org.junit.Test;
+import org.onosproject.cluster.Leadership;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.event.Change;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -37,25 +34,20 @@
/**
* Unit tests for {@link AtomixLeaderElector}.
*/
-public class AtomixLeaderElectorTest extends AtomixTestBase {
+public class AtomixLeaderElectorTest extends AtomixTestBase<AtomixLeaderElector> {
NodeId node1 = new NodeId("node1");
NodeId node2 = new NodeId("node2");
NodeId node3 = new NodeId("node3");
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(3);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
+ @Override
+ protected RaftService createService() {
+ return new AtomixLeaderElectorService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixLeaderElector.class);
+ protected AtomixLeaderElector createPrimitive(RaftProxy proxy) {
+ return new AtomixLeaderElector(proxy);
}
@Test
@@ -64,18 +56,15 @@
}
private void leaderElectorRunTests() throws Throwable {
- Atomix client1 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-run",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-run");
elector1.run("foo", node1).thenAccept(result -> {
assertEquals(node1, result.leaderNodeId());
assertEquals(1, result.leader().term());
assertEquals(1, result.candidates().size());
assertEquals(node1, result.candidates().get(0));
}).join();
- Atomix client2 = createAtomixClient();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-run",
- AtomixLeaderElector.class).join();
+
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-run");
elector2.run("foo", node2).thenAccept(result -> {
assertEquals(node1, result.leaderNodeId());
assertEquals(1, result.leader().term());
@@ -91,13 +80,9 @@
}
private void leaderElectorWithdrawTests() throws Throwable {
- Atomix client1 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-withdraw",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-withdraw");
elector1.run("foo", node1).join();
- Atomix client2 = createAtomixClient();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-withdraw",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-withdraw");
elector2.run("foo", node2).join();
LeaderEventListener listener1 = new LeaderEventListener();
@@ -121,6 +106,14 @@
assertEquals(1, result.newValue().candidates().size());
assertEquals(node2, result.newValue().candidates().get(0));
}).join();
+
+ Leadership leadership1 = elector1.getLeadership("foo").join();
+ assertEquals(node2, leadership1.leader().nodeId());
+ assertEquals(1, leadership1.candidates().size());
+
+ Leadership leadership2 = elector2.getLeadership("foo").join();
+ assertEquals(node2, leadership2.leader().nodeId());
+ assertEquals(1, leadership2.candidates().size());
}
@Test
@@ -129,15 +122,9 @@
}
private void leaderElectorAnointTests() throws Throwable {
- Atomix client1 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-anoint",
- AtomixLeaderElector.class).join();
- Atomix client2 = createAtomixClient();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-anoint",
- AtomixLeaderElector.class).join();
- Atomix client3 = createAtomixClient();
- AtomixLeaderElector elector3 = client3.getResource("test-elector-anoint",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-anoint");
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-anoint");
+ AtomixLeaderElector elector3 = newPrimitive("test-elector-anoint");
elector1.run("foo", node1).join();
elector2.run("foo", node2).join();
@@ -185,15 +172,9 @@
}
private void leaderElectorPromoteTests() throws Throwable {
- AtomixClient client1 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-promote",
- AtomixLeaderElector.class).join();
- AtomixClient client2 = createAtomixClient();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-promote",
- AtomixLeaderElector.class).join();
- AtomixClient client3 = createAtomixClient();
- AtomixLeaderElector elector3 = client3.getResource("test-elector-promote",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-promote");
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-promote");
+ AtomixLeaderElector elector3 = newPrimitive("test-elector-promote");
elector1.run("foo", node1).join();
elector2.run("foo", node2).join();
@@ -245,17 +226,13 @@
}
private void leaderElectorLeaderSessionCloseTests() throws Throwable {
- AtomixClient client1 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-leader-session-close",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-leader-session-close");
elector1.run("foo", node1).join();
- Atomix client2 = createAtomixClient();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-leader-session-close",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-leader-session-close");
LeaderEventListener listener = new LeaderEventListener();
elector2.run("foo", node2).join();
elector2.addChangeListener(listener).join();
- client1.close();
+ elector1.proxy.close();
listener.nextEvent().thenAccept(result -> {
assertEquals(node2, result.newValue().leaderNodeId());
assertEquals(1, result.newValue().candidates().size());
@@ -269,17 +246,13 @@
}
private void leaderElectorNonLeaderSessionCloseTests() throws Throwable {
- Atomix client1 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-non-leader-session-close",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-non-leader-session-close");
elector1.run("foo", node1).join();
- AtomixClient client2 = createAtomixClient();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-non-leader-session-close",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-non-leader-session-close");
LeaderEventListener listener = new LeaderEventListener();
elector2.run("foo", node2).join();
elector1.addChangeListener(listener).join();
- client2.close().join();
+ elector2.proxy.close().join();
listener.nextEvent().thenAccept(result -> {
assertEquals(node1, result.newValue().leaderNodeId());
assertEquals(1, result.newValue().candidates().size());
@@ -293,12 +266,8 @@
}
private void leaderElectorQueryTests() throws Throwable {
- Atomix client1 = createAtomixClient();
- Atomix client2 = createAtomixClient();
- AtomixLeaderElector elector1 = client1.getResource("test-elector-query",
- AtomixLeaderElector.class).join();
- AtomixLeaderElector elector2 = client2.getResource("test-elector-query",
- AtomixLeaderElector.class).join();
+ AtomixLeaderElector elector1 = newPrimitive("test-elector-query");
+ AtomixLeaderElector elector2 = newPrimitive("test-elector-query");
elector1.run("foo", node1).join();
elector2.run("foo", node2).join();
elector2.run("bar", node2).join();
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java
index f7a5007..7073bb5 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java
@@ -15,133 +15,490 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import com.google.common.util.concurrent.Uninterruptibles;
-
-import io.atomix.AtomixClient;
-import io.atomix.catalyst.serializer.Serializer;
-import io.atomix.catalyst.transport.Address;
-import io.atomix.catalyst.transport.local.LocalServerRegistry;
-import io.atomix.catalyst.transport.netty.NettyTransport;
-import io.atomix.copycat.client.CopycatClient;
-import io.atomix.copycat.server.CopycatServer;
-import io.atomix.copycat.server.storage.Storage;
-import io.atomix.copycat.server.storage.StorageLevel;
-import io.atomix.manager.internal.ResourceManagerState;
-import io.atomix.resource.ResourceType;
-import org.onlab.junit.TestTools;
-import org.onosproject.store.primitives.impl.CatalystSerializers;
-
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.time.Instant;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import com.google.common.collect.Lists;
+import io.atomix.protocols.raft.RaftClient;
+import io.atomix.protocols.raft.RaftError;
+import io.atomix.protocols.raft.RaftServer;
+import io.atomix.protocols.raft.ReadConsistency;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.cluster.RaftMember;
+import io.atomix.protocols.raft.cluster.impl.DefaultRaftMember;
+import io.atomix.protocols.raft.event.RaftEvent;
+import io.atomix.protocols.raft.event.impl.DefaultEventType;
+import io.atomix.protocols.raft.operation.OperationType;
+import io.atomix.protocols.raft.operation.RaftOperation;
+import io.atomix.protocols.raft.operation.impl.DefaultOperationId;
+import io.atomix.protocols.raft.protocol.AppendRequest;
+import io.atomix.protocols.raft.protocol.AppendResponse;
+import io.atomix.protocols.raft.protocol.CloseSessionRequest;
+import io.atomix.protocols.raft.protocol.CloseSessionResponse;
+import io.atomix.protocols.raft.protocol.CommandRequest;
+import io.atomix.protocols.raft.protocol.CommandResponse;
+import io.atomix.protocols.raft.protocol.ConfigureRequest;
+import io.atomix.protocols.raft.protocol.ConfigureResponse;
+import io.atomix.protocols.raft.protocol.InstallRequest;
+import io.atomix.protocols.raft.protocol.InstallResponse;
+import io.atomix.protocols.raft.protocol.JoinRequest;
+import io.atomix.protocols.raft.protocol.JoinResponse;
+import io.atomix.protocols.raft.protocol.KeepAliveRequest;
+import io.atomix.protocols.raft.protocol.KeepAliveResponse;
+import io.atomix.protocols.raft.protocol.LeaveRequest;
+import io.atomix.protocols.raft.protocol.LeaveResponse;
+import io.atomix.protocols.raft.protocol.MetadataRequest;
+import io.atomix.protocols.raft.protocol.MetadataResponse;
+import io.atomix.protocols.raft.protocol.OpenSessionRequest;
+import io.atomix.protocols.raft.protocol.OpenSessionResponse;
+import io.atomix.protocols.raft.protocol.PollRequest;
+import io.atomix.protocols.raft.protocol.PollResponse;
+import io.atomix.protocols.raft.protocol.PublishRequest;
+import io.atomix.protocols.raft.protocol.QueryRequest;
+import io.atomix.protocols.raft.protocol.QueryResponse;
+import io.atomix.protocols.raft.protocol.RaftResponse;
+import io.atomix.protocols.raft.protocol.ReconfigureRequest;
+import io.atomix.protocols.raft.protocol.ReconfigureResponse;
+import io.atomix.protocols.raft.protocol.ResetRequest;
+import io.atomix.protocols.raft.protocol.VoteRequest;
+import io.atomix.protocols.raft.protocol.VoteResponse;
+import io.atomix.protocols.raft.proxy.CommunicationStrategy;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
+import io.atomix.protocols.raft.session.SessionId;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.log.entry.CloseSessionEntry;
+import io.atomix.protocols.raft.storage.log.entry.CommandEntry;
+import io.atomix.protocols.raft.storage.log.entry.ConfigurationEntry;
+import io.atomix.protocols.raft.storage.log.entry.InitializeEntry;
+import io.atomix.protocols.raft.storage.log.entry.KeepAliveEntry;
+import io.atomix.protocols.raft.storage.log.entry.MetadataEntry;
+import io.atomix.protocols.raft.storage.log.entry.OpenSessionEntry;
+import io.atomix.protocols.raft.storage.log.entry.QueryEntry;
+import io.atomix.protocols.raft.storage.system.Configuration;
+import io.atomix.storage.StorageLevel;
+import org.junit.After;
+import org.junit.Before;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.PartitionId;
+import org.onosproject.store.primitives.impl.RaftClientCommunicator;
+import org.onosproject.store.primitives.impl.RaftServerCommunicator;
+import org.onosproject.store.service.Serializer;
/**
* Base class for various Atomix tests.
+ *
+ * @param <T> the Raft primitive type being tested
*/
-public abstract class AtomixTestBase {
- protected static LocalServerRegistry registry = new LocalServerRegistry();
- protected static List<Address> members = new ArrayList<>();
- protected static List<CopycatClient> copycatClients = new ArrayList<>();
- protected static List<CopycatServer> copycatServers = new ArrayList<>();
- protected static List<AtomixClient> atomixClients = new ArrayList<>();
- protected static List<CopycatServer> atomixServers = new ArrayList<>();
- protected static Serializer serializer = CatalystSerializers.getSerializer();
- protected static AtomicInteger port = new AtomicInteger(49200);
+public abstract class AtomixTestBase<T extends AbstractRaftPrimitive> {
+
+ private static final Serializer PROTOCOL_SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(OpenSessionRequest.class)
+ .register(OpenSessionResponse.class)
+ .register(CloseSessionRequest.class)
+ .register(CloseSessionResponse.class)
+ .register(KeepAliveRequest.class)
+ .register(KeepAliveResponse.class)
+ .register(QueryRequest.class)
+ .register(QueryResponse.class)
+ .register(CommandRequest.class)
+ .register(CommandResponse.class)
+ .register(MetadataRequest.class)
+ .register(MetadataResponse.class)
+ .register(JoinRequest.class)
+ .register(JoinResponse.class)
+ .register(LeaveRequest.class)
+ .register(LeaveResponse.class)
+ .register(ConfigureRequest.class)
+ .register(ConfigureResponse.class)
+ .register(ReconfigureRequest.class)
+ .register(ReconfigureResponse.class)
+ .register(InstallRequest.class)
+ .register(InstallResponse.class)
+ .register(PollRequest.class)
+ .register(PollResponse.class)
+ .register(VoteRequest.class)
+ .register(VoteResponse.class)
+ .register(AppendRequest.class)
+ .register(AppendResponse.class)
+ .register(PublishRequest.class)
+ .register(ResetRequest.class)
+ .register(RaftResponse.Status.class)
+ .register(RaftError.class)
+ .register(RaftError.Type.class)
+ .register(ReadConsistency.class)
+ .register(byte[].class)
+ .register(long[].class)
+ .register(CloseSessionEntry.class)
+ .register(CommandEntry.class)
+ .register(ConfigurationEntry.class)
+ .register(InitializeEntry.class)
+ .register(KeepAliveEntry.class)
+ .register(MetadataEntry.class)
+ .register(OpenSessionEntry.class)
+ .register(QueryEntry.class)
+ .register(RaftOperation.class)
+ .register(RaftEvent.class)
+ .register(DefaultEventType.class)
+ .register(DefaultOperationId.class)
+ .register(OperationType.class)
+ .register(ReadConsistency.class)
+ .register(ArrayList.class)
+ .register(LinkedList.class)
+ .register(Collections.emptyList().getClass())
+ .register(HashSet.class)
+ .register(DefaultRaftMember.class)
+ .register(MemberId.class)
+ .register(SessionId.class)
+ .register(RaftMember.Type.class)
+ .register(RaftMember.Status.class)
+ .register(Instant.class)
+ .register(Configuration.class)
+ .register(AtomixAtomicCounterMapOperations.class)
+ .register(AtomixConsistentMapEvents.class)
+ .register(AtomixConsistentMapOperations.class)
+ .register(AtomixConsistentSetMultimapOperations.class)
+ .register(AtomixConsistentSetMultimapEvents.class)
+ .register(AtomixConsistentTreeMapEvents.class)
+ .register(AtomixConsistentTreeMapOperations.class)
+ .register(AtomixCounterOperations.class)
+ .register(AtomixDocumentTreeEvents.class)
+ .register(AtomixDocumentTreeOperations.class)
+ .register(AtomixLeaderElectorEvents.class)
+ .register(AtomixLeaderElectorOperations.class)
+ .register(AtomixWorkQueueEvents.class)
+ .register(AtomixWorkQueueOperations.class)
+ .build());
+
+ private static final Serializer STORAGE_SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+ .register(CloseSessionEntry.class)
+ .register(CommandEntry.class)
+ .register(ConfigurationEntry.class)
+ .register(InitializeEntry.class)
+ .register(KeepAliveEntry.class)
+ .register(MetadataEntry.class)
+ .register(OpenSessionEntry.class)
+ .register(QueryEntry.class)
+ .register(RaftOperation.class)
+ .register(ReadConsistency.class)
+ .register(AtomixAtomicCounterMapOperations.class)
+ .register(AtomixConsistentMapOperations.class)
+ .register(AtomixConsistentSetMultimapOperations.class)
+ .register(AtomixConsistentTreeMapOperations.class)
+ .register(AtomixCounterOperations.class)
+ .register(AtomixDocumentTreeOperations.class)
+ .register(AtomixLeaderElectorOperations.class)
+ .register(AtomixWorkQueueOperations.class)
+ .register(ArrayList.class)
+ .register(HashSet.class)
+ .register(DefaultRaftMember.class)
+ .register(MemberId.class)
+ .register(RaftMember.Type.class)
+ .register(RaftMember.Status.class)
+ .register(Instant.class)
+ .register(Configuration.class)
+ .register(byte[].class)
+ .register(long[].class)
+ .build());
+
+ protected TestClusterCommunicationServiceFactory communicationServiceFactory;
+ protected List<RaftMember> members = Lists.newCopyOnWriteArrayList();
+ protected List<RaftClient> clients = Lists.newCopyOnWriteArrayList();
+ protected List<RaftServer> servers = Lists.newCopyOnWriteArrayList();
+ protected int nextId;
/**
- * Creates a new resource state machine.
+ * Creates the primitive service.
*
- * @return A new resource state machine.
+ * @return the primitive service
*/
- protected abstract ResourceType resourceType();
+ protected abstract RaftService createService();
+
+ /**
+ * Creates a new primitive.
+ *
+ * @param name the primitive name
+ * @return the primitive instance
+ */
+ protected T newPrimitive(String name) {
+ RaftClient client = createClient();
+ RaftProxy proxy = client.newProxyBuilder()
+ .withName(name)
+ .withServiceType("test")
+ .withReadConsistency(readConsistency())
+ .withCommunicationStrategy(communicationStrategy())
+ .build()
+ .open()
+ .join();
+ return createPrimitive(proxy);
+ }
+
+ /**
+ * Creates a new primitive instance.
+ *
+ * @param proxy the primitive proxy
+ * @return the primitive instance
+ */
+ protected abstract T createPrimitive(RaftProxy proxy);
+
+ /**
+ * Returns the proxy read consistency.
+ *
+ * @return the primitive read consistency
+ */
+ protected ReadConsistency readConsistency() {
+ return ReadConsistency.LINEARIZABLE;
+ }
+
+ /**
+ * Returns the proxy communication strategy.
+ *
+ * @return the primitive communication strategy
+ */
+ protected CommunicationStrategy communicationStrategy() {
+ return CommunicationStrategy.LEADER;
+ }
+
+ @Before
+ public void prepare() {
+ members.clear();
+ clients.clear();
+ servers.clear();
+ communicationServiceFactory = new TestClusterCommunicationServiceFactory();
+ createServers(3);
+ }
+
+ @After
+ public void cleanup() {
+ shutdown();
+ }
+
+ /**
+ * Shuts down clients and servers.
+ */
+ private void shutdown() {
+ clients.forEach(c -> {
+ try {
+ c.close().get(10, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ }
+ });
+
+ servers.forEach(s -> {
+ try {
+ if (s.isRunning()) {
+ s.shutdown().get(10, TimeUnit.SECONDS);
+ }
+ } catch (Exception e) {
+ }
+ });
+
+ Path directory = Paths.get("target/primitives/");
+ if (Files.exists(directory)) {
+ try {
+ Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Returns the next unique member identifier.
+ *
+ * @return The next unique member identifier.
+ */
+ private MemberId nextMemberId() {
+ return MemberId.from(String.valueOf(++nextId));
+ }
/**
* Returns the next server address.
*
+ * @param type The startup member type.
* @return The next server address.
*/
- private static Address nextAddress() {
- Address address = new Address("127.0.0.1",
- TestTools.findAvailablePort(port.getAndIncrement()));
- members.add(address);
- return address;
+ private RaftMember nextMember(RaftMember.Type type) {
+ return new TestMember(nextMemberId(), type);
}
/**
- * Creates a set of Copycat servers.
+ * Creates a set of Raft servers.
*/
- protected static List<CopycatServer> createCopycatServers(int nodes)
- throws Throwable {
- List<CopycatServer> servers = new ArrayList<>();
-
- List<Address> members = new ArrayList<>();
+ protected List<RaftServer> createServers(int nodes) {
+ List<RaftServer> servers = new ArrayList<>();
for (int i = 0; i < nodes; i++) {
- Address address = nextAddress();
- members.add(address);
- CopycatServer server = createCopycatServer(address);
- if (members.size() <= 1) {
- server.bootstrap().join();
- } else {
- server.join(members).join();
- }
+ members.add(nextMember(RaftMember.Type.ACTIVE));
+ }
+
+ CountDownLatch latch = new CountDownLatch(nodes);
+ for (int i = 0; i < nodes; i++) {
+ RaftServer server = createServer(members.get(i));
+ server.bootstrap(members.stream().map(RaftMember::memberId).collect(Collectors.toList()))
+ .thenRun(latch::countDown);
servers.add(server);
}
+ try {
+ latch.await(30000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
return servers;
}
/**
- * Creates a Copycat server.
+ * Creates a Raft server.
*/
- protected static CopycatServer createCopycatServer(Address address) {
- CopycatServer server = CopycatServer.builder(address)
- .withTransport(NettyTransport.builder().withThreads(1).build())
- .withStorage(Storage.builder()
- .withStorageLevel(StorageLevel.MEMORY)
- .build())
- .withStateMachine(ResourceManagerState::new)
- .withSerializer(serializer.clone())
- .build();
- copycatServers.add(server);
+ private RaftServer createServer(RaftMember member) {
+ RaftServer.Builder builder = RaftServer.newBuilder(member.memberId())
+ .withType(member.getType())
+ .withProtocol(new RaftServerCommunicator(
+ PartitionId.from(1),
+ PROTOCOL_SERIALIZER,
+ communicationServiceFactory.newCommunicationService(NodeId.nodeId(member.memberId().id()))))
+ .withStorage(RaftStorage.newBuilder()
+ .withStorageLevel(StorageLevel.MEMORY)
+ .withDirectory(new File(String.format("target/primitives/%s", member.memberId())))
+ .withSerializer(new AtomixSerializerAdapter(STORAGE_SERIALIZER))
+ .withMaxSegmentSize(1024 * 1024)
+ .build())
+ .addService("test", this::createService);
+
+ RaftServer server = builder.build();
+ servers.add(server);
return server;
}
- public static void clearTests() throws Exception {
- registry = new LocalServerRegistry();
- members = new ArrayList<>();
+ /**
+ * Creates a Raft client.
+ */
+ private RaftClient createClient() {
+ MemberId memberId = nextMemberId();
+ RaftClient client = RaftClient.newBuilder()
+ .withMemberId(memberId)
+ .withProtocol(new RaftClientCommunicator(
+ PartitionId.from(1),
+ PROTOCOL_SERIALIZER,
+ communicationServiceFactory.newCommunicationService(NodeId.nodeId(memberId.id()))))
+ .build();
- CompletableFuture<Void> closeClients =
- CompletableFuture.allOf(atomixClients.stream()
- .map(AtomixClient::close)
- .toArray(CompletableFuture[]::new));
- closeClients.join();
-
- CompletableFuture<Void> closeServers =
- CompletableFuture.allOf(copycatServers.stream()
- .map(CopycatServer::shutdown)
- .toArray(CompletableFuture[]::new));
- closeServers.join();
-
- atomixClients.clear();
- copycatServers.clear();
+ client.connect(members.stream().map(RaftMember::memberId).collect(Collectors.toList())).join();
+ clients.add(client);
+ return client;
}
-
/**
- * Creates a Atomix client.
+ * Test member.
*/
- protected AtomixClient createAtomixClient() {
- CountDownLatch latch = new CountDownLatch(1);
- AtomixClient client = AtomixClient.builder()
- .withTransport(NettyTransport.builder().withThreads(1).build())
- .withSerializer(serializer.clone())
- .build();
- client.connect(members).thenRun(latch::countDown);
- atomixClients.add(client);
- Uninterruptibles.awaitUninterruptibly(latch);
- return client;
+ public static class TestMember implements RaftMember {
+ private final MemberId memberId;
+ private final Type type;
+
+ public TestMember(MemberId memberId, Type type) {
+ this.memberId = memberId;
+ this.type = type;
+ }
+
+ @Override
+ public MemberId memberId() {
+ return memberId;
+ }
+
+ @Override
+ public int hash() {
+ return memberId.hashCode();
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public void addTypeChangeListener(Consumer<Type> listener) {
+
+ }
+
+ @Override
+ public void removeTypeChangeListener(Consumer<Type> listener) {
+
+ }
+
+ @Override
+ public Status getStatus() {
+ return Status.AVAILABLE;
+ }
+
+ @Override
+ public Instant getLastUpdated() {
+ return Instant.now();
+ }
+
+ @Override
+ public void addStatusChangeListener(Consumer<Status> listener) {
+
+ }
+
+ @Override
+ public void removeStatusChangeListener(Consumer<Status> listener) {
+
+ }
+
+ @Override
+ public CompletableFuture<Void> promote() {
+ return null;
+ }
+
+ @Override
+ public CompletableFuture<Void> promote(Type type) {
+ return null;
+ }
+
+ @Override
+ public CompletableFuture<Void> demote() {
+ return null;
+ }
+
+ @Override
+ public CompletableFuture<Void> demote(Type type) {
+ return null;
+ }
+
+ @Override
+ public CompletableFuture<Void> remove() {
+ return null;
+ }
}
}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java
new file mode 100644
index 0000000..44ebd52
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import io.atomix.protocols.raft.ReadConsistency;
+import io.atomix.protocols.raft.cluster.MemberId;
+import io.atomix.protocols.raft.impl.RaftServerContext;
+import io.atomix.protocols.raft.protocol.RaftServerProtocol;
+import io.atomix.protocols.raft.service.ServiceId;
+import io.atomix.protocols.raft.service.ServiceType;
+import io.atomix.protocols.raft.service.impl.DefaultCommit;
+import io.atomix.protocols.raft.service.impl.DefaultServiceContext;
+import io.atomix.protocols.raft.session.SessionId;
+import io.atomix.protocols.raft.session.impl.RaftSessionContext;
+import io.atomix.protocols.raft.storage.RaftStorage;
+import io.atomix.protocols.raft.storage.snapshot.Snapshot;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotReader;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotStore;
+import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter;
+import io.atomix.storage.StorageLevel;
+import io.atomix.time.WallClockTimestamp;
+import io.atomix.utils.concurrent.ThreadContext;
+import org.junit.Test;
+import org.onosproject.store.service.Task;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.mock;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.ADD;
+import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.TAKE;
+import static org.onosproject.store.service.DistributedPrimitive.Type.WORK_QUEUE;
+
+/**
+ * Work queue service test.
+ */
+public class AtomixWorkQueueServiceTest {
+ @Test
+ public void testSnapshot() throws Exception {
+ SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder()
+ .withPrefix("test")
+ .withStorageLevel(StorageLevel.MEMORY)
+ .build());
+ Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp());
+
+ DefaultServiceContext context = mock(DefaultServiceContext.class);
+ expect(context.serviceType()).andReturn(ServiceType.from(WORK_QUEUE.name())).anyTimes();
+ expect(context.serviceName()).andReturn("test").anyTimes();
+ expect(context.serviceId()).andReturn(ServiceId.from(1)).anyTimes();
+ expect(context.executor()).andReturn(mock(ThreadContext.class)).anyTimes();
+
+ RaftServerContext server = mock(RaftServerContext.class);
+ expect(server.getProtocol()).andReturn(mock(RaftServerProtocol.class));
+
+ replay(context, server);
+
+ RaftSessionContext session = new RaftSessionContext(
+ SessionId.from(1),
+ MemberId.from("1"),
+ "test",
+ ServiceType.from(WORK_QUEUE.name()),
+ ReadConsistency.LINEARIZABLE,
+ 5000,
+ context,
+ server);
+
+ AtomixWorkQueueService service = new AtomixWorkQueueService();
+ service.init(context);
+
+ service.add(new DefaultCommit<>(
+ 2,
+ ADD,
+ new AtomixWorkQueueOperations.Add(Arrays.asList("Hello world!".getBytes())),
+ session,
+ System.currentTimeMillis()));
+
+ try (SnapshotWriter writer = snapshot.openWriter()) {
+ service.snapshot(writer);
+ }
+
+ snapshot.complete();
+
+ service = new AtomixWorkQueueService();
+ service.init(context);
+
+ try (SnapshotReader reader = snapshot.openReader()) {
+ service.install(reader);
+ }
+
+ Collection<Task<byte[]>> value = service.take(new DefaultCommit<>(
+ 2,
+ TAKE,
+ new AtomixWorkQueueOperations.Take(1),
+ session,
+ System.currentTimeMillis()));
+ assertNotNull(value);
+ assertEquals(1, value.size());
+ assertArrayEquals("Hello world!".getBytes(), value.iterator().next().payload());
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java
index 3675373..5357b04 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java
@@ -15,13 +15,6 @@
*/
package org.onosproject.store.primitives.resources.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import io.atomix.Atomix;
-import io.atomix.AtomixClient;
-import io.atomix.resource.ResourceType;
-
import java.time.Duration;
import java.util.Arrays;
import java.util.UUID;
@@ -30,48 +23,43 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import com.google.common.util.concurrent.Uninterruptibles;
+import io.atomix.protocols.raft.proxy.RaftProxy;
+import io.atomix.protocols.raft.service.RaftService;
import org.junit.Test;
import org.onlab.util.Tools;
import org.onosproject.store.service.Task;
import org.onosproject.store.service.WorkQueueStats;
-import com.google.common.util.concurrent.Uninterruptibles;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
/**
* Unit tests for {@link AtomixWorkQueue}.
*/
-public class AtomixWorkQueueTest extends AtomixTestBase {
-
+public class AtomixWorkQueueTest extends AtomixTestBase<AtomixWorkQueue> {
private static final Duration DEFAULT_PROCESSING_TIME = Duration.ofMillis(100);
private static final byte[] DEFAULT_PAYLOAD = "hello world".getBytes();
- @BeforeClass
- public static void preTestSetup() throws Throwable {
- createCopycatServers(1);
- }
-
- @AfterClass
- public static void postTestCleanup() throws Exception {
- clearTests();
+ @Override
+ protected RaftService createService() {
+ return new AtomixWorkQueueService();
}
@Override
- protected ResourceType resourceType() {
- return new ResourceType(AtomixWorkQueue.class);
+ protected AtomixWorkQueue createPrimitive(RaftProxy proxy) {
+ return new AtomixWorkQueue(proxy);
}
@Test
public void testAdd() throws Throwable {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
byte[] item = DEFAULT_PAYLOAD;
queue1.addOne(item).join();
- Atomix atomix2 = createAtomixClient();
- AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue2 = newPrimitive(queueName);
byte[] task2 = DEFAULT_PAYLOAD;
queue2.addOne(task2).join();
@@ -84,8 +72,7 @@
@Test
public void testAddMultiple() throws Throwable {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
byte[] item1 = DEFAULT_PAYLOAD;
byte[] item2 = DEFAULT_PAYLOAD;
queue1.addMultiple(Arrays.asList(item1, item2)).join();
@@ -99,13 +86,11 @@
@Test
public void testTakeAndComplete() throws Throwable {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
byte[] item1 = DEFAULT_PAYLOAD;
queue1.addOne(item1).join();
- Atomix atomix2 = createAtomixClient();
- AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue2 = newPrimitive(queueName);
Task<byte[]> removedTask = queue2.take().join();
WorkQueueStats stats = queue2.stats().join();
@@ -128,13 +113,11 @@
@Test
public void testUnexpectedClientClose() throws Throwable {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
byte[] item1 = DEFAULT_PAYLOAD;
queue1.addOne(item1).join();
- AtomixClient atomix2 = createAtomixClient();
- AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue2 = newPrimitive(queueName);
queue2.take().join();
WorkQueueStats stats = queue1.stats().join();
@@ -142,7 +125,7 @@
assertEquals(1, stats.totalInProgress());
assertEquals(0, stats.totalCompleted());
- atomix2.close().join();
+ queue2.proxy.close().join();
stats = queue1.stats().join();
assertEquals(1, stats.totalPending());
@@ -153,15 +136,13 @@
@Test
public void testAutomaticTaskProcessing() throws Throwable {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
Executor executor = Executors.newSingleThreadExecutor();
CountDownLatch latch1 = new CountDownLatch(1);
queue1.registerTaskProcessor(s -> latch1.countDown(), 2, executor);
- AtomixClient atomix2 = createAtomixClient();
- AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue2 = newPrimitive(queueName);
byte[] item1 = DEFAULT_PAYLOAD;
queue2.addOne(item1).join();
@@ -189,13 +170,11 @@
@Test
public void testDestroy() {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
byte[] item = DEFAULT_PAYLOAD;
queue1.addOne(item).join();
- Atomix atomix2 = createAtomixClient();
- AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue2 = newPrimitive(queueName);
byte[] task2 = DEFAULT_PAYLOAD;
queue2.addOne(task2).join();
@@ -215,8 +194,7 @@
@Test
public void testCompleteAttemptWithIncorrectSession() {
String queueName = UUID.randomUUID().toString();
- Atomix atomix1 = createAtomixClient();
- AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue1 = newPrimitive(queueName);
byte[] item = DEFAULT_PAYLOAD;
queue1.addOne(item).join();
@@ -224,8 +202,7 @@
String taskId = task.taskId();
// Create another client and get a handle to the same queue.
- Atomix atomix2 = createAtomixClient();
- AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join();
+ AtomixWorkQueue queue2 = newPrimitive(queueName);
// Attempt completing the task with new client and verify task is not completed
queue2.complete(taskId).join();
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java
new file mode 100644
index 0000000..bb4b78c
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import com.google.common.collect.Maps;
+import io.atomix.utils.concurrent.Futures;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
+import org.onosproject.store.cluster.messaging.MessageSubject;
+import org.onosproject.store.cluster.messaging.MessagingException;
+
+/**
+ * Cluster communication service implementation used for testing.
+ */
+public class TestClusterCommunicationService implements ClusterCommunicationService {
+ private final NodeId localNodeId;
+ private final Map<NodeId, TestClusterCommunicationService> nodes;
+ private final Map<MessageSubject, Function<byte[], CompletableFuture<byte[]>>> subscribers =
+ Maps.newConcurrentMap();
+
+ public TestClusterCommunicationService(NodeId localNodeId, Map<NodeId, TestClusterCommunicationService> nodes) {
+ this.localNodeId = localNodeId;
+ this.nodes = nodes;
+ nodes.put(localNodeId, this);
+ }
+
+ @Override
+ public <M> void broadcast(M message, MessageSubject subject, Function<M, byte[]> encoder) {
+ nodes.forEach((nodeId, node) -> {
+ if (!nodeId.equals(localNodeId)) {
+ node.handle(subject, encoder.apply(message));
+ }
+ });
+ }
+
+ @Override
+ public <M> void broadcastIncludeSelf(M message, MessageSubject subject, Function<M, byte[]> encoder) {
+ nodes.values().forEach(node -> node.handle(subject, encoder.apply(message)));
+ }
+
+ @Override
+ public <M> CompletableFuture<Void> unicast(
+ M message, MessageSubject subject, Function<M, byte[]> encoder, NodeId toNodeId) {
+ TestClusterCommunicationService node = nodes.get(toNodeId);
+ if (node != null) {
+ node.handle(subject, encoder.apply(message));
+ }
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public <M> void multicast(M message, MessageSubject subject, Function<M, byte[]> encoder, Set<NodeId> nodeIds) {
+ nodes.values().stream()
+ .filter(n -> nodeIds.contains(n))
+ .forEach(n -> n.handle(subject, encoder.apply(message)));
+ }
+
+ @Override
+ public <M, R> CompletableFuture<R> sendAndReceive(
+ M message,
+ MessageSubject subject,
+ Function<M, byte[]> encoder,
+ Function<byte[], R> decoder,
+ NodeId toNodeId) {
+ TestClusterCommunicationService node = nodes.get(toNodeId);
+ if (node == null) {
+ return Futures.exceptionalFuture(new MessagingException.NoRemoteHandler());
+ }
+ return node.handle(subject, encoder.apply(message)).thenApply(decoder);
+ }
+
+ private CompletableFuture<byte[]> handle(MessageSubject subject, byte[] message) {
+ Function<byte[], CompletableFuture<byte[]>> subscriber = subscribers.get(subject);
+ if (subscriber != null) {
+ return subscriber.apply(message);
+ }
+ return Futures.exceptionalFuture(new MessagingException.NoRemoteHandler());
+ }
+
+ private boolean isSubscriber(MessageSubject subject) {
+ return subscribers.containsKey(subject);
+ }
+
+ @Override
+ public <M, R> void addSubscriber(
+ MessageSubject subject,
+ Function<byte[], M> decoder,
+ Function<M, R> handler,
+ Function<R, byte[]> encoder,
+ Executor executor) {
+ subscribers.put(subject, message -> {
+ CompletableFuture<byte[]> future = new CompletableFuture<>();
+ executor.execute(() -> {
+ try {
+ future.complete(encoder.apply(handler.apply(decoder.apply(message))));
+ } catch (Exception e) {
+ future.completeExceptionally(new MessagingException.RemoteHandlerFailure());
+ }
+ });
+ return future;
+ });
+ }
+
+ @Override
+ public <M, R> void addSubscriber(
+ MessageSubject subject,
+ Function<byte[], M> decoder,
+
+ Function<M, CompletableFuture<R>> handler, Function<R, byte[]> encoder) {
+ subscribers.put(subject, message -> {
+ CompletableFuture<byte[]> future = new CompletableFuture<>();
+ try {
+ handler.apply(decoder.apply(message)).whenComplete((result, error) -> {
+ if (error == null) {
+ future.complete(encoder.apply(result));
+ } else {
+ future.completeExceptionally(new MessagingException.RemoteHandlerFailure());
+ }
+ });
+ } catch (Exception e) {
+ future.completeExceptionally(new MessagingException.RemoteHandlerFailure());
+ }
+ return future;
+ });
+ }
+
+ @Override
+ public <M> void addSubscriber(
+ MessageSubject subject,
+ Function<byte[], M> decoder,
+ Consumer<M> handler,
+ Executor executor) {
+ subscribers.put(subject, message -> {
+ try {
+ handler.accept(decoder.apply(message));
+ } catch (Exception e) {
+ return Futures.exceptionalFuture(new MessagingException.RemoteHandlerFailure());
+ }
+ return Futures.completedFuture(null);
+ });
+ }
+
+ @Override
+ public void removeSubscriber(MessageSubject subject) {
+ subscribers.remove(subject);
+ }
+
+ @Override
+ public void addSubscriber(MessageSubject subject, ClusterMessageHandler subscriber, ExecutorService executor) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java
new file mode 100644
index 0000000..819c9c3
--- /dev/null
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.store.primitives.resources.impl;
+
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+
+/**
+ * Test cluster communication service factory.
+ */
+public class TestClusterCommunicationServiceFactory {
+ private final Map<NodeId, TestClusterCommunicationService> nodes = Maps.newConcurrentMap();
+
+ /**
+ * Creates a new cluster communication service for the given node.
+ *
+ * @param localNodeId the node for which to create the service
+ * @return the communication service for the given node
+ */
+ public ClusterCommunicationService newCommunicationService(NodeId localNodeId) {
+ return new TestClusterCommunicationService(localNodeId, nodes);
+ }
+}