BUG-5280: add cds-access-api identifiers 70/38670/16
authorRobert Varga <rovarga@cisco.com>
Wed, 11 May 2016 14:42:32 +0000 (16:42 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Fri, 20 May 2016 08:36:20 +0000 (08:36 +0000)
This patch adds a new component, which will hold the API exposed
by the CDS backend and used by the CDS frontend.

Change-Id: If0a2a6372b6cbbe898d2413bcd35259f7900a90a
Signed-off-by: Robert Varga <rovarga@cisco.com>
16 files changed:
features/mdsal/src/main/features/features.xml
opendaylight/md-sal/cds-access-api/pom.xml [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/LocalHistoryIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/TransactionIdentifier.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/package-info.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/AbstractIdentifierTest.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifierTest.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/FrontendIdentifierTest.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/MemberNameTest.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-artifacts/pom.xml
opendaylight/md-sal/pom.xml
opendaylight/md-sal/sal-distributed-datastore/pom.xml

index d5d06f69e405e4a4491e6a729a504439809ed748..ca048233733f6db1fb9ea216a33fa6d747e68ff9 100644 (file)
@@ -81,6 +81,7 @@
     <feature name ='odl-mdsal-distributed-datastore' version='${project.version}'>
         <feature version='${project.version}'>odl-mdsal-broker-local</feature>
         <feature version='${project.version}'>odl-mdsal-clustering-commons</feature>
+        <bundle>mvn:org.opendaylight.controller/cds-access-api/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.controller/sal-distributed-datastore/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.controller/sal-cluster-admin/{{VERSION}}</bundle>
     </feature>
diff --git a/opendaylight/md-sal/cds-access-api/pom.xml b/opendaylight/md-sal/cds-access-api/pom.xml
new file mode 100644 (file)
index 0000000..05f3c8a
--- /dev/null
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>bundle-parent</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>cds-access-api</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yangtools-artifacts</artifactId>
+                <version>1.0.0-SNAPSHOT</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.typesafe.akka</groupId>
+            <artifactId>akka-actor_${scala.version}</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>concepts</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <scm>
+        <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+        <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+        <tag>HEAD</tag>
+        <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture:Clustering</url>
+    </scm>
+</project>
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifier.java
new file mode 100644 (file)
index 0000000..b2016d8
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * A cluster-wide unique identifier of a frontend instance. This identifier discerns between individual incarnations
+ * of a particular frontend.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class ClientIdentifier<T extends FrontendType> implements Identifier {
+    private static final class Proxy<T extends FrontendType> implements Externalizable {
+        private static final long serialVersionUID = 1L;
+        private FrontendIdentifier<T> frontendId;
+        private long generation;
+
+        public Proxy() {
+            // Needed for Externalizable
+        }
+
+        Proxy(final FrontendIdentifier<T> frontendId, final long generation) {
+            this.frontendId = Preconditions.checkNotNull(frontendId);
+            this.generation = generation;
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeObject(frontendId);
+            out.writeLong(generation);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            frontendId = (FrontendIdentifier<T>) in.readObject();
+            generation = in.readLong();
+        }
+
+        private Object readResolve() {
+            return new ClientIdentifier<>(frontendId, generation);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private final FrontendIdentifier<T> frontendId;
+    private final long generation;
+
+    ClientIdentifier(final FrontendIdentifier<T> frontendId, final long generation) {
+        this.frontendId = Preconditions.checkNotNull(frontendId);
+        this.generation = generation;
+    }
+
+    public static <T extends FrontendType> ClientIdentifier<T> create(final FrontendIdentifier<T> frontendId,
+            final long generation) {
+        return new ClientIdentifier<>(frontendId, generation);
+    }
+
+    public FrontendIdentifier<T> getFrontendId() {
+        return frontendId;
+    }
+
+    public long getGeneration() {
+        return generation;
+    }
+
+    @Override
+    public int hashCode() {
+        return frontendId.hashCode() * 31 + Long.hashCode(generation);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ClientIdentifier)) {
+            return false;
+        }
+
+        final ClientIdentifier<?> other = (ClientIdentifier<?>) o;
+        return generation == other.generation && frontendId.equals(other.frontendId);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(ClientIdentifier.class).add("frontend", frontendId)
+                .add("generation", Long.toUnsignedString(generation)).toString();
+    }
+
+    private Object writeReplace() {
+        return new Proxy<>(frontendId, generation);
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendIdentifier.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendIdentifier.java
new file mode 100644 (file)
index 0000000..bd16e4b
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Objects;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * A cluster-wide unique identifier of a frontend type located at a cluster member.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class FrontendIdentifier<T extends FrontendType> implements Identifier {
+    private static final class Proxy<T extends FrontendType> implements Externalizable {
+        private static final long serialVersionUID = 1L;
+        private MemberName memberName;
+        private T clientType;
+
+        public Proxy() {
+            // Needed for Externalizable
+        }
+
+        Proxy(final MemberName memberName, final T clientType) {
+            this.memberName = Preconditions.checkNotNull(memberName);
+            this.clientType = Preconditions.checkNotNull(clientType);
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeObject(memberName);
+            out.writeObject(clientType);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            memberName = (MemberName) in.readObject();
+            clientType = (T) in.readObject();
+        }
+
+        private Object readResolve() {
+            return new FrontendIdentifier<>(memberName, clientType);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private final MemberName memberName;
+    private final T clientType;
+
+    FrontendIdentifier(final MemberName memberName, final T clientType) {
+        this.clientType = Preconditions.checkNotNull(clientType);
+        this.memberName = Preconditions.checkNotNull(memberName);
+    }
+
+    public static <T extends FrontendType> FrontendIdentifier<T> create(MemberName memberName, final T clientType) {
+        return new FrontendIdentifier<>(memberName, clientType);
+    }
+
+    public T getClientType() {
+        return clientType;
+    }
+
+    public MemberName getMemberName() {
+        return memberName;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(memberName, clientType);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof FrontendIdentifier)) {
+            return false;
+        }
+
+        final FrontendIdentifier<?> other = (FrontendIdentifier<?>) o;
+        return memberName.equals(other.memberName) && clientType.equals(other.clientType);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(FrontendIdentifier.class).add("member", memberName)
+                .add("clientType", clientType).toString();
+    }
+
+    private Object writeReplace() {
+        return new Proxy<>(memberName, clientType);
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/FrontendType.java
new file mode 100644 (file)
index 0000000..27db820
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.annotations.Beta;
+import java.util.regex.Pattern;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * An {@link Identifier} identifying a data store frontend type, which is able to access the data store backend.
+ * Frontend implementations need to define this identifier so that multiple clients existing on a member node can be
+ * discerned.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface FrontendType extends Identifier {
+    Pattern SIMPLE_STRING_PATTERN = Pattern.compile("[a-zA-Z-_.*+:=,!~';]+");
+
+    /**
+     * Return a string representation of this frontend type. Unlike {@link #toString()}, returned string has
+     * restricted rules on what it can contain:
+     * - US-ASCII letters
+     * - special characters: -_.*+:=,!~';
+     *
+     * A validation pattern for this string is available as {@link #SIMPLE_STRING_PATTERN}.
+     */
+    @Nonnull String toSimpleString();
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/LocalHistoryIdentifier.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/LocalHistoryIdentifier.java
new file mode 100644 (file)
index 0000000..330362e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * Globally-unique identifier of a local history.
+ *
+ * @param <T> Frontend type
+ *
+ * @author Robert Varga
+ */
+public final class LocalHistoryIdentifier<T extends FrontendType> implements Identifier {
+    private static final class Proxy<T extends FrontendType> implements Externalizable {
+        private static final long serialVersionUID = 1L;
+        private ClientIdentifier<T> clientId;
+        private long historyId;
+
+        public Proxy() {
+            // For Externalizable
+        }
+
+        Proxy(final ClientIdentifier<T> frontendId, final long historyId) {
+            this.clientId = Preconditions.checkNotNull(frontendId);
+            this.historyId = historyId;
+        }
+
+        @Override
+        public void writeExternal(final ObjectOutput out) throws IOException {
+            out.writeObject(clientId);
+            out.writeLong(historyId);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+            clientId = (ClientIdentifier<T>) in.readObject();
+            historyId = in.readLong();
+        }
+
+        private Object readResolve() {
+            return new LocalHistoryIdentifier<>(clientId, historyId);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private final ClientIdentifier<T> clientId;
+    private final long historyId;
+
+    public LocalHistoryIdentifier(final ClientIdentifier<T> frontendId, final long historyId) {
+        this.clientId = Preconditions.checkNotNull(frontendId);
+        this.historyId = historyId;
+    }
+
+    public ClientIdentifier<T> getClienId() {
+        return clientId;
+    }
+
+    public long getHistoryId() {
+        return historyId;
+    }
+
+    @Override
+    public int hashCode() {
+        return clientId.hashCode() * 31 + Long.hashCode(historyId);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof LocalHistoryIdentifier)) {
+            return false;
+        }
+
+        final LocalHistoryIdentifier<?> other = (LocalHistoryIdentifier<?>) o;
+        return historyId == other.historyId && clientId.equals(other.clientId);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(LocalHistoryIdentifier.class).add("client", clientId)
+                .add("history", Long.toUnsignedString(historyId)).toString();
+    }
+
+    private Object writeReplace() {
+        return new Proxy<>(clientId, historyId);
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/MemberName.java
new file mode 100644 (file)
index 0000000..ceec555
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.base.Verify;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.nio.charset.StandardCharsets;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * Type-safe encapsulation of a cluster member name.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class MemberName implements Comparable<MemberName>, Identifier {
+    private static final class Proxy implements Externalizable {
+        private static final long serialVersionUID = 1L;
+        private byte[] serialized;
+
+        public Proxy() {
+            // For Externalizable
+        }
+
+        Proxy(final String name) {
+            serialized = name.getBytes(StandardCharsets.UTF_8);
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeInt(serialized.length);
+            out.write(serialized);
+        }
+
+        @Override
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+            serialized = new byte[in.readInt()];
+            in.readFully(serialized);
+        }
+
+        private Object readResolve() {
+            // TODO: consider caching instances here
+            return new MemberName(new String(serialized, StandardCharsets.UTF_8), this);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private final String name;
+    private volatile Proxy proxy;
+
+    MemberName(final String name) {
+        this.name = Preconditions.checkNotNull(name);
+    }
+
+    MemberName(final String name, final Proxy proxy) {
+        this.name = Preconditions.checkNotNull(name);
+        this.proxy = Verify.verifyNotNull(proxy);
+    }
+
+    public static MemberName forName(final String name) {
+        Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
+        // TODO: consider caching instances here
+        return new MemberName(name);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        return this == o || (o instanceof MemberName && name.equals(((MemberName)o).name));
+    }
+
+    @Override
+    public int compareTo(final MemberName o) {
+        return this == o ? 0 : name.compareTo(o.name);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(MemberName.class).add("name", name).toString();
+    }
+
+    Object writeReplace() {
+        Proxy ret = proxy;
+        if (ret == null) {
+            // We do not really care if multiple threads race here
+            ret = new Proxy(name);
+            proxy = ret;
+        }
+
+        return ret;
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/TransactionIdentifier.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/TransactionIdentifier.java
new file mode 100644 (file)
index 0000000..2aaf826
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * Globally-unique identifier of a transaction.
+ *
+ * @param <T> Frontend type
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class TransactionIdentifier<T extends FrontendType> implements Identifier {
+    private static final class Proxy<T extends FrontendType> implements Externalizable {
+        private static final long serialVersionUID = 1L;
+        private LocalHistoryIdentifier<T> historyId;
+        private long transactionId;
+
+        public Proxy() {
+            // For Externalizable
+        }
+
+        Proxy(final LocalHistoryIdentifier<T> historyId, final long transactionId) {
+            this.historyId = Preconditions.checkNotNull(historyId);
+            this.transactionId = transactionId;
+        }
+
+        @Override
+        public void writeExternal(final ObjectOutput out) throws IOException {
+            out.writeObject(historyId);
+            out.writeLong(transactionId);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+            historyId = (LocalHistoryIdentifier<T>) in.readObject();
+            transactionId = in.readLong();
+        }
+
+        private Object readResolve() {
+            return new TransactionIdentifier<>(historyId, transactionId);
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+    private final LocalHistoryIdentifier<T> historyId;
+    private final long transactionId;
+
+    public TransactionIdentifier(final @Nonnull LocalHistoryIdentifier<T> historyId, final long transactionId) {
+        this.historyId = Preconditions.checkNotNull(historyId);
+        this.transactionId = transactionId;
+    }
+
+    public LocalHistoryIdentifier<T> getHistoryId() {
+        return historyId;
+    }
+
+    public long getTransactionId() {
+        return transactionId;
+    }
+
+    @Override
+    public int hashCode() {
+        return historyId.hashCode() * 31 + Long.hashCode(transactionId);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof TransactionIdentifier)) {
+            return false;
+        }
+
+        final TransactionIdentifier<?> other = (TransactionIdentifier<?>) o;
+        return transactionId == other.transactionId && historyId.equals(other.historyId);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(TransactionIdentifier.class).add("history", historyId)
+                .add("transaction", transactionId).toString();
+    }
+
+    private Object writeReplace() {
+        return new Proxy<>(historyId, transactionId);
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/package-info.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/package-info.java
new file mode 100644 (file)
index 0000000..3184e07
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * This package defines basic concepts used to interact with the CDS backend implementation.
+ *
+ * @author Robert Varga
+ */
+package org.opendaylight.controller.cluster.access.concepts;
diff --git a/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/AbstractIdentifierTest.java b/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/AbstractIdentifierTest.java
new file mode 100644 (file)
index 0000000..34ce489
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.junit.Test;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+public abstract class AbstractIdentifierTest<T extends Identifier> {
+    abstract T object();
+    abstract T differentObject();
+    abstract T equalObject();
+
+    @Test
+    public final void testEquals() {
+        assertTrue(object().equals(object()));
+        assertTrue(object().equals(equalObject()));
+        assertFalse(object().equals(null));
+        assertFalse(object().equals("dummy"));
+        assertFalse(object().equals(differentObject()));
+    }
+
+    @Test
+    public final void testHashCode() {
+        assertEquals(object().hashCode(), equalObject().hashCode());
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T copy(T o) throws IOException, ClassNotFoundException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+            oos.writeObject(o);
+        }
+
+        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
+            return (T) ois.readObject();
+        }
+    }
+
+    @Test
+    public final void testSerialization() throws Exception {
+        assertTrue(object().equals(copy(object())));
+        assertTrue(object().equals(copy(equalObject())));
+        assertFalse(differentObject().equals(copy(object())));
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifierTest.java b/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/ClientIdentifierTest.java
new file mode 100644 (file)
index 0000000..666c2b0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+public class ClientIdentifierTest extends AbstractIdentifierTest<ClientIdentifier<?>> {
+    private static final FrontendIdentifier<?> FRONTEND =
+            new FrontendIdentifier<>(MemberName.forName("test"), FrontendIdentifierTest.ONE_FRONTEND_TYPE);
+
+    private static final ClientIdentifier<?> OBJECT = new ClientIdentifier<>(FRONTEND, 0);
+    private static final ClientIdentifier<?> DIFFERENT_OBJECT = new ClientIdentifier<>(FRONTEND, 1);
+    private static final ClientIdentifier<?> EQUAL_OBJECT = new ClientIdentifier<>(FRONTEND, 0);
+
+    @Override
+    ClientIdentifier<?> object() {
+        return OBJECT;
+    }
+
+    @Override
+    ClientIdentifier<?> differentObject() {
+        return DIFFERENT_OBJECT;
+    }
+
+    @Override
+    ClientIdentifier<?> equalObject() {
+        return EQUAL_OBJECT;
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/FrontendIdentifierTest.java b/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/FrontendIdentifierTest.java
new file mode 100644 (file)
index 0000000..5611bb5
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+public class FrontendIdentifierTest extends AbstractIdentifierTest<FrontendIdentifier<?>> {
+    static final FrontendType ONE_FRONTEND_TYPE = new FrontendType() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public String toSimpleString() {
+            return "one";
+        }
+
+        private Object readResolve() {
+            return ONE_FRONTEND_TYPE;
+        }
+    };
+    static final FrontendType OTHER_FRONTEND_TYPE = new FrontendType() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public String toSimpleString() {
+            return "two";
+        }
+
+        private Object readResolve() {
+            return OTHER_FRONTEND_TYPE;
+        }
+    };
+
+    private static final MemberName MEMBER = MemberName.forName("test");
+    private static final FrontendIdentifier<?> OBJECT = new FrontendIdentifier<>(MEMBER, ONE_FRONTEND_TYPE);
+    private static final FrontendIdentifier<?> DIFFERENT_OBJECT = new FrontendIdentifier<>(MEMBER, OTHER_FRONTEND_TYPE);
+    private static final FrontendIdentifier<?> EQUAL_OBJECT = new FrontendIdentifier<>(MEMBER, ONE_FRONTEND_TYPE);
+
+    @Override
+    FrontendIdentifier<?> object() {
+        return OBJECT;
+    }
+
+    @Override
+    FrontendIdentifier<?> differentObject() {
+        return DIFFERENT_OBJECT;
+    }
+
+    @Override
+    FrontendIdentifier<?> equalObject() {
+        return EQUAL_OBJECT;
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/MemberNameTest.java b/opendaylight/md-sal/cds-access-api/src/test/java/org/opendaylight/controller/cluster/access/concepts/MemberNameTest.java
new file mode 100644 (file)
index 0000000..e5031fc
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class MemberNameTest extends AbstractIdentifierTest<MemberName> {
+    private static final MemberName OBJECT = MemberName.forName("test1");
+    private static final MemberName DIFFERENT_OBJECT = MemberName.forName("test2");
+    private static final MemberName EQUAL_OBJECT = MemberName.forName("test1");
+
+    @Override
+    MemberName object() {
+        return OBJECT;
+    }
+
+    @Override
+    MemberName differentObject() {
+        return DIFFERENT_OBJECT;
+    }
+
+    @Override
+    MemberName equalObject() {
+        return EQUAL_OBJECT;
+    }
+
+
+    @Test
+    public void testCompareTo() {
+        assertEquals(0, object().compareTo(object()));
+        assertEquals(0, object().compareTo(equalObject()));
+        assertTrue(object().compareTo(differentObject()) < 0);
+        assertTrue(differentObject().compareTo(object()) > 0);
+    }
+
+    @Test
+    public void testGetName() {
+        assertEquals("test1", OBJECT.getName());
+    }
+}
index e94b96262923e5aec5b47666b22d5a183922ce26..5a3b1bf9b0a9c652cffb5c978cb73090d8d63e04 100644 (file)
                 <artifactId>sal-akka-raft-example</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.opendaylight.controller</groupId>
+                <artifactId>cds-access-api</artifactId>
+                <version>1.0.0-SNAPSHOT</version>
+            </dependency>
 
             <!-- RESTCONF -->
             <dependency>
index 03aa18cf6908c4c21c8dd1a9091a7edc5d20d2af..489ec7bdc3997d636289f288b3010892cc432476 100644 (file)
@@ -55,8 +55,8 @@
     <!--InMemory DOM DataStore-->
     <module>sal-inmemory-datastore</module>
 
-    <!--sal-protocolbuffer-encoding is now part of sal-clutering-commons-->
     <module>sal-clustering-commons</module>
+    <module>cds-access-api</module>
 
     <!-- sal clustering configuration -->
     <module>sal-clustering-config</module>
index 8ce090e702b73625c920f2f29ade81eff13d7fbc..b138574add54d52b50e037445c343b158c372436 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-akka-raft</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>cds-access-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-akka-raft-example</artifactId>