BUG-5280: introduce Request/Response and related concepts 79/39479/48
authorRobert Varga <rovarga@cisco.com>
Thu, 26 May 2016 11:36:35 +0000 (13:36 +0200)
committerTom Pantelis <tpanteli@brocade.com>
Thu, 16 Jun 2016 05:30:55 +0000 (05:30 +0000)
This adds a versioned message concept and Request/Response concepts
built on top of it. Serialization uses the serializable proxy pattern,
with one proxy class per ABI version.

For ActorRefs we rely on the fact that serialization of these
messages should only ever occur in the context of JavaSerializer,
so we can use its #currentSystem() and perform ActorRef serialization
without having to go through writeObject().

Change-Id: Ic2701646bf3fc34c0d8d1dea278bbf63324a6786
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractMessageProxy.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractRequestFailureProxy.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractRequestProxy.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractResponseProxy.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractSuccessProxy.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Request.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestException.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestFailure.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestSuccess.java [new file with mode: 0644]
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Response.java [new file with mode: 0644]

diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractMessageProxy.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractMessageProxy.java
new file mode 100644 (file)
index 0000000..04e7037
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.Verify;
+import java.io.DataInput;
+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.WritableIdentifier;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Abstract Externalizable proxy for use with {@link Message} subclasses.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ * @param <C> Message class
+ */
+abstract class AbstractMessageProxy<T extends WritableIdentifier, C extends Message<T, C>> implements Externalizable {
+    private static final long serialVersionUID = 1L;
+    private T target;
+    private long sequence;
+    private long retry;
+
+    protected AbstractMessageProxy() {
+        // For Externalizable
+    }
+
+    AbstractMessageProxy(final @Nonnull C message) {
+        this.target = message.getTarget();
+        this.sequence = message.getSequence();
+        this.retry = message.getRetry();
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        target.writeTo(out);
+        WritableObjects.writeLongs(out, sequence, retry);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        target = Verify.verifyNotNull(readTarget(in));
+
+        final byte header = WritableObjects.readLongHeader(in);
+        sequence = WritableObjects.readFirstLong(in, header);
+        retry = WritableObjects.readSecondLong(in, header);
+    }
+
+    protected final Object readResolve() {
+        return Verify.verifyNotNull(createMessage(target, sequence, retry));
+    }
+
+    protected abstract @Nonnull T readTarget(@Nonnull DataInput in) throws IOException;
+    abstract @Nonnull C createMessage(@Nonnull T target, long sequence, long retry);
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractRequestFailureProxy.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractRequestFailureProxy.java
new file mode 100644 (file)
index 0000000..51e1c72
--- /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 com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * Abstract Externalizable proxy for use with {@link RequestFailure} subclasses.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ */
+@Beta
+public abstract class AbstractRequestFailureProxy<T extends WritableIdentifier, C extends RequestFailure<T, C>>
+        extends AbstractResponseProxy<T, C> {
+    private static final long serialVersionUID = 1L;
+    private RequestException cause;
+
+    protected AbstractRequestFailureProxy() {
+        // For Externalizable
+    }
+
+    protected AbstractRequestFailureProxy(final @Nonnull C failure) {
+        super(failure);
+        this.cause = failure.getCause();
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        super.writeExternal(out);
+        out.writeObject(cause);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        super.readExternal(in);
+        cause = (RequestException) in.readObject();
+    }
+
+    @Override
+    final C createResponse(final T target, final long sequence, final long retry) {
+        return createFailure(target, sequence, retry, cause);
+    }
+
+    protected abstract @Nonnull C createFailure(@Nonnull T target, long sequence, long retry,
+            @Nonnull RequestException cause);
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractRequestProxy.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractRequestProxy.java
new file mode 100644 (file)
index 0000000..48b1602
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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 akka.actor.ActorRef;
+import akka.serialization.JavaSerializer;
+import akka.serialization.Serialization;
+import com.google.common.annotations.Beta;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * Abstract Externalizable proxy for use with {@link Request} subclasses.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ */
+@Beta
+public abstract class AbstractRequestProxy<T extends WritableIdentifier, C extends Request<T, C>>
+        extends AbstractMessageProxy<T, C> {
+    private static final long serialVersionUID = 1L;
+    private ActorRef replyTo;
+
+    protected AbstractRequestProxy() {
+        // For Externalizable
+    }
+
+    protected AbstractRequestProxy(final @Nonnull C request) {
+        super(request);
+        this.replyTo = request.getReplyTo();
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        super.writeExternal(out);
+        out.writeUTF(Serialization.serializedActorPath(replyTo));
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        super.readExternal(in);
+        replyTo = JavaSerializer.currentSystem().value().provider().resolveActorRef(in.readUTF());
+    }
+
+    @Override
+    protected final C createMessage(final T target, final long sequence, final long retry) {
+        return createRequest(target, sequence, retry, replyTo);
+    }
+
+    protected abstract @Nonnull C createRequest(@Nonnull T target, long sequence, long retry, @Nonnull ActorRef replyTo);
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractResponseProxy.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractResponseProxy.java
new file mode 100644 (file)
index 0000000..08c3135
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * Abstract Externalizable proxy class to use with {@link Response} subclasses.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ * @param <C> Message class
+ */
+abstract class AbstractResponseProxy<T extends WritableIdentifier, C extends Response<T, C>>
+        extends AbstractMessageProxy<T, C> {
+    private static final long serialVersionUID = 1L;
+
+    AbstractResponseProxy() {
+        // for Externalizable
+    }
+
+    AbstractResponseProxy(final @Nonnull C response) {
+        super(response);
+    }
+
+    @Override
+    final C createMessage(final T target, final long sequence, final long retry) {
+        return createResponse(target, sequence, retry);
+    }
+
+    abstract @Nonnull C createResponse(@Nonnull T target, long sequence, long retry);
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractSuccessProxy.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/AbstractSuccessProxy.java
new file mode 100644 (file)
index 0000000..bb58785
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.io.Externalizable;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * Abstract Externalizable proxy for use with {@link RequestSuccess} subclasses.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ */
+@Beta
+public abstract class AbstractSuccessProxy<T extends WritableIdentifier, C extends RequestSuccess<T, C>>
+        extends AbstractResponseProxy<T, C> implements Externalizable {
+    private static final long serialVersionUID = 1L;
+
+    protected AbstractSuccessProxy() {
+        // For Externalizable
+    }
+
+    protected AbstractSuccessProxy(final @Nonnull C success) {
+        super(success);
+    }
+
+    @Override
+    final C createResponse(final T target, final long sequence, final long retry) {
+        return createSuccess(target, sequence, retry);
+    }
+
+    protected abstract @Nonnull C createSuccess(@Nonnull T target, long sequence, long retry);
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Message.java
new file mode 100644 (file)
index 0000000..87b0e6e
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * 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.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.cluster.access.ABIVersion;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * An abstract concept of a Message. This class cannot be instantiated directly, use its specializations {@link Request}
+ * and {@link Response}.
+ *
+ * Messages have a target and a sequence number. Sequence numbers are expected to be assigned monotonically on a
+ * per-target basis, hence two targets can observe the same sequence number.
+ *
+ * This class includes explicit versioning for forward- and backward- compatibility of serialization format. This is
+ * achieved by using the serialization proxy pattern. Subclasses are in complete control of what proxy is used to
+ * serialize a particular object on the wire. This class can serve as an explicit version marker, hence no further
+ * action is necessary in the deserialization path.
+ *
+ * For the serialization path an explicit call from the user is required to select the appropriate serialization
+ * version. This is done via {@link #toVersion(ABIVersion)} method, which should return a copy of this object with
+ * the requested ABI version recorded and should return the appropriate serialization proxy.
+ *
+ * This workflow allows least disturbance across ABI versions, as all messages not affected by a ABI version bump
+ * will remain working with the same serialization format for the new ABI version.
+ *
+ * Note that this class specifies the {@link Immutable} contract, which means that all subclasses must follow this API
+ * contract.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ * @param <C> Message type
+ */
+@Beta
+public abstract class Message<T extends WritableIdentifier, C extends Message<T, C>> implements Immutable,
+        Serializable {
+    private static final long serialVersionUID = 1L;
+    private final T target;
+    private final long sequence;
+    private final ABIVersion version;
+    private final long retry;
+
+    private Message(final ABIVersion version, final T target, final long sequence, final long retry) {
+        this.target = Preconditions.checkNotNull(target);
+        this.version = Preconditions.checkNotNull(version);
+        this.sequence = sequence;
+        this.retry = retry;
+    }
+
+    Message(final T target, final long sequence, final long retry) {
+        this(ABIVersion.current(), target, sequence, retry);
+    }
+
+    Message(final C msg, final ABIVersion version) {
+        this(version, msg.getTarget(), msg.getSequence(), msg.getRetry());
+    }
+
+    Message(final C msg, final long retry) {
+        this(msg.getVersion(), msg.getTarget(), msg.getSequence(), retry);
+    }
+
+    /**
+     * Get the target identifier for this message.
+     *
+     * @return Target identifier
+     */
+    public final @Nonnull T getTarget() {
+        return target;
+    }
+
+    /**
+     * Get the message sequence of this message.
+     *
+     * @return Message sequence
+     */
+    public final long getSequence() {
+        return sequence;
+    }
+
+    @VisibleForTesting
+    public final ABIVersion getVersion() {
+        return version;
+    }
+
+    /**
+     * Get the message retry counter.
+     *
+     * @return Retry counter
+     */
+    public final long getRetry() {
+        return retry;
+    }
+
+    /**
+     * Return a message which will end up being serialized in the specified {@link ABIVersion}.
+     *
+     * @param version Request {@link ABIVersion}
+     * @return A new message which will use ABIVersion as its serialization.
+     */
+    @SuppressWarnings("unchecked")
+    public final @Nonnull C toVersion(final @Nonnull ABIVersion version) {
+        if (this.version == version) {
+            return (C)this;
+        }
+
+        switch (version) {
+            case BORON:
+                return Verify.verifyNotNull(cloneAsVersion(version));
+            case TEST_PAST_VERSION:
+            case TEST_FUTURE_VERSION:
+                // Fall-through to throw
+                break;
+        }
+
+        throw new IllegalArgumentException("Unhandled ABI version " + version);
+    }
+
+    /**
+     * Create a copy of this message which will serialize to a stream corresponding to the specified method. This
+     * method should be implemented by the concrete final message class and should invoke the equivalent of
+     * {@link #Message(Message, ABIVersion)}.
+     *
+     * @param version target ABI version
+     * @return A message with the specified serialization stream
+     * @throws IllegalArgumentException if this message does not support the target ABI
+     */
+    protected abstract @Nonnull C cloneAsVersion(@Nonnull ABIVersion version);
+
+    /**
+     * Return a message which will have the retry counter incremented by one.
+     *
+     * @return A message with the specified retry counter
+     */
+    public final @Nonnull C incrementRetry() {
+        return Verify.verifyNotNull(cloneAsRetry(retry +1));
+    }
+
+    /**
+     * Create a copy of this message which will have its retry count bumped. This method should be implemented by
+     * the concrete final message class and should invoked the equivalent of {@link #Message(Message, long)}.
+     *
+     * @param retry new retry count
+     * @return A message with the specified retry counter
+     */
+    protected abstract @Nonnull C cloneAsRetry(long retry);
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    /**
+     * Add attributes to the output of {@link #toString()}. Subclasses wanting to contribute additional information
+     * should override this method. Any null attributes will be omitted from the output.
+     *
+     * @param toStringHelper a {@link ToStringHelper} instance
+     * @return The {@link ToStringHelper} passed in as argument
+     * @throws NullPointerException if toStringHelper is null
+     */
+    protected @Nonnull ToStringHelper addToStringAttributes(final @Nonnull ToStringHelper toStringHelper) {
+        return toStringHelper.add("target", target).add("sequence", Long.toUnsignedString(sequence, 16));
+    }
+
+    /**
+     * Instantiate a serialization proxy for this object for the target ABI version. Implementations should return
+     * different objects for incompatible {@link ABIVersion}s. This method should never fail, as any compatibility
+     * checks should have been done by {@link #cloneAsVersion(ABIVersion)}.
+     *
+     * @param version Requested ABI version
+     * @return Proxy for this object
+     */
+    abstract @Nonnull AbstractMessageProxy<T, C> externalizableProxy(@Nonnull ABIVersion version);
+
+    protected final Object writeReplace() {
+        return externalizableProxy(version);
+    }
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Request.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Request.java
new file mode 100644 (file)
index 0000000..d0d6324
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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 akka.actor.ActorRef;
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.cluster.access.ABIVersion;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * A request message concept. Upon receipt of this message, the recipient will respond with either
+ * a {@link RequestSuccess} or a {@link RequestFailure} message.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ * @param <C> Message type
+ */
+@Beta
+public abstract class Request<T extends WritableIdentifier, C extends Request<T, C>> extends Message<T, C> {
+    private static final long serialVersionUID = 1L;
+    private final ActorRef replyTo;
+
+    protected Request(final @Nonnull T target, final long sequence, final long retry, final @Nonnull ActorRef replyTo) {
+        super(target, sequence, retry);
+        this.replyTo = Preconditions.checkNotNull(replyTo);
+    }
+
+    protected Request(final @Nonnull C request, final @Nonnull ABIVersion version) {
+        super(request, version);
+        this.replyTo = Preconditions.checkNotNull(request.getReplyTo());
+    }
+
+    protected Request(final C request, final long retry) {
+        super(request, retry);
+        this.replyTo = Preconditions.checkNotNull(request.getReplyTo());
+    }
+
+    /**
+     * Return the return address where responses to this request should be directed to.
+     *
+     * @return Original requestor
+     */
+    public final @Nonnull ActorRef getReplyTo() {
+        return replyTo;
+    }
+
+    /**
+     * Return a {@link RequestFailure} for this request, caused by a {@link RequestException}.
+     *
+     * @param cause Failure cause
+     * @return {@link RequestFailure} corresponding to this request
+     */
+    public abstract @Nonnull RequestFailure<T, ?> toRequestFailure(final @Nonnull RequestException cause);
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return super.addToStringAttributes(toStringHelper).add("replyTo", replyTo);
+    }
+
+    @Override
+    protected abstract AbstractRequestProxy<T, C> externalizableProxy(@Nonnull ABIVersion version);
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestException.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestException.java
new file mode 100644 (file)
index 0000000..ec51912
--- /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;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+
+/**
+ * A failure cause behind a {@link RequestFailure} to process a {@link Request}.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public abstract class RequestException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    protected RequestException(final @Nonnull String message) {
+        super(Preconditions.checkNotNull(message));
+    }
+
+    protected RequestException(final @Nonnull String message, final @Nonnull Exception cause) {
+        super(Preconditions.checkNotNull(message), Preconditions.checkNotNull(cause));
+    }
+
+    public abstract boolean isRetriable();
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestFailure.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestFailure.java
new file mode 100644 (file)
index 0000000..7fd9009
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.ToStringHelper;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.cluster.access.ABIVersion;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * A failure response to a {@link Request}. Contains a {@link RequestException} detailing the cause for this failure.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ * @param <C> Message class
+ */
+@Beta
+public abstract class RequestFailure<T extends WritableIdentifier, C extends RequestFailure<T, C>> extends Response<T, C> {
+    private static final long serialVersionUID = 1L;
+    private final RequestException cause;
+
+    protected RequestFailure(final @Nonnull C failure, final @Nonnull ABIVersion version) {
+        super(failure, version);
+        this.cause = Preconditions.checkNotNull(failure.getCause());
+    }
+
+    protected RequestFailure(final @Nonnull T target, final long sequence, final long retry,
+            final @Nonnull RequestException cause) {
+        super(target, sequence, retry);
+        this.cause = Preconditions.checkNotNull(cause);
+    }
+
+    /**
+     * Return the failure cause.
+     *
+     * @return Failure cause.
+     */
+    public final @Nonnull RequestException getCause() {
+        return cause;
+    }
+
+    /**
+     * Return an indication of whether this a hard failure. Hard failures must not be retried but need to be treated
+     * as authoritative response to a request.
+     *
+     * @return True if this represents a hard failure, false otherwise.
+     */
+    public final boolean isHardFailure() {
+        return !cause.isRetriable();
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return super.addToStringAttributes(toStringHelper).add("cause", cause);
+    }
+
+    @Override
+    protected abstract AbstractRequestFailureProxy<T, C> externalizableProxy(@Nonnull ABIVersion version);
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestSuccess.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RequestSuccess.java
new file mode 100644 (file)
index 0000000..04f8ca3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import org.opendaylight.controller.cluster.access.ABIVersion;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * A successful reply to a {@link Request}.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ */
+@Beta
+public abstract class RequestSuccess<T extends WritableIdentifier, C extends RequestSuccess<T, C>> extends
+        Response<T, C> {
+    private static final long serialVersionUID = 1L;
+
+    protected RequestSuccess(final @Nonnull C success, final @Nonnull ABIVersion version) {
+        super(success, version);
+    }
+
+    protected RequestSuccess(final @Nonnull T target, final long sequence, final long retry) {
+        super(target, sequence, retry);
+    }
+
+    @Override
+    protected abstract AbstractSuccessProxy<T, C> externalizableProxy(@Nonnull ABIVersion version);
+}
diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Response.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/Response.java
new file mode 100644 (file)
index 0000000..07acb27
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import org.opendaylight.controller.cluster.access.ABIVersion;
+import org.opendaylight.yangtools.concepts.WritableIdentifier;
+
+/**
+ * Abstract counterpart to a {@link Request}. This class should not be instantiated directly, but rather through
+ * {@link RequestFailure} and {@link RequestSuccess}, which provide appropriate specialization. It is visible purely for
+ * the purpose of allowing to check if an object is either of those specializations with a single instanceof check.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Target identifier type
+ * @param <C> Message type
+ */
+@Beta
+public abstract class Response<T extends WritableIdentifier, C extends Response<T, C>> extends Message<T, C> {
+    private static final long serialVersionUID = 1L;
+
+    Response(final @Nonnull C response, final @Nonnull ABIVersion version) {
+        super(response, version);
+    }
+
+    Response(final @Nonnull T target, final long sequence, final long retry) {
+        super(target, sequence, retry);
+    }
+
+    @Override
+    protected final C cloneAsRetry(final long retry) {
+        throw new UnsupportedOperationException("Responses do not support retries");
+    }
+
+    @Override
+    abstract AbstractResponseProxy<T, C> externalizableProxy(@Nonnull ABIVersion version);
+}