[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);
+    }
+}