Switch to use PayloadVersion.CHLORINE_SR2
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / persisted / AbstractIdentifiablePayload.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.datastore.persisted;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.io.ByteStreams;
15 import java.io.DataInput;
16 import java.io.Externalizable;
17 import java.io.IOException;
18 import java.io.ObjectInput;
19 import java.io.ObjectOutput;
20 import java.io.Serializable;
21 import java.util.function.Function;
22 import org.apache.commons.lang3.SerializationUtils;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.opendaylight.controller.cluster.raft.messages.IdentifiablePayload;
25 import org.opendaylight.yangtools.concepts.Identifiable;
26 import org.opendaylight.yangtools.concepts.Identifier;
27
28 /**
29  * Abstract base class for {@link IdentifiablePayload}s which hold a single {@link Identifier}.
30  *
31  * @author Robert Varga
32  */
33 public abstract class AbstractIdentifiablePayload<T extends Identifier> extends IdentifiablePayload<T>
34         implements Serializable {
35     /**
36      * An {@link Externalizable} with default implementations we expect our implementations to comply with. On-wire
37      * serialization format is defined by {@link #bytes()}.
38      */
39     protected interface SerialForm extends Externalizable {
40         /**
41          * Return the serial form of this object contents, corresponding to
42          * {@link AbstractIdentifiablePayload#serialized}.
43          *
44          * @return Serialized form
45          */
46         byte[] bytes();
47
48         /**
49          * Resolve this proxy to an actual {@link AbstractIdentifiablePayload}.
50          *
51          * @return A payload.
52          */
53         @java.io.Serial
54         Object readResolve();
55
56         /**
57          * Restore state from specified serialized form.
58          *
59          * @param newBytes Serialized form, as returned by {@link #bytes()}
60          * @throws IOException when a deserialization problem occurs
61          */
62         void readExternal(byte[] newBytes) throws IOException;
63
64         /**
65          * {@inheritDoc}
66          *
67          * <p>
68          * The default implementation is canonical and should never be overridden.
69          */
70         @Override
71         default void readExternal(final ObjectInput in) throws IOException {
72             final var bytes = new byte[in.readInt()];
73             in.readFully(bytes);
74             readExternal(bytes);
75         }
76
77         /**
78          * {@inheritDoc}
79          *
80          * <p>
81          * The default implementation is canonical and should never be overridden.
82          */
83         @Override
84         default void writeExternal(final ObjectOutput out) throws IOException {
85             final var bytes = bytes();
86             out.writeInt(bytes.length);
87             out.write(bytes);
88         }
89     }
90
91     @Deprecated(since = "7.0.0", forRemoval = true)
92     protected abstract static class AbstractProxy<T extends Identifier> implements SerialForm {
93         @java.io.Serial
94         private static final long serialVersionUID = 1L;
95
96         private byte[] serialized;
97         private T identifier;
98
99         public AbstractProxy() {
100             // For Externalizable
101         }
102
103         protected AbstractProxy(final byte[] serialized) {
104             this.serialized = requireNonNull(serialized);
105         }
106
107         @Override
108         public final byte[] bytes() {
109             return serialized;
110         }
111
112         @Override
113         public final void readExternal(final byte[] bytes) throws IOException {
114             serialized = requireNonNull(bytes);
115             identifier = verifyNotNull(readIdentifier(ByteStreams.newDataInput(serialized)));
116         }
117
118         @Override
119         public final Object readResolve() {
120             return verifyNotNull(createObject(identifier, serialized));
121         }
122
123         protected abstract @NonNull T readIdentifier(@NonNull DataInput in) throws IOException;
124
125         @SuppressWarnings("checkstyle:hiddenField")
126         protected abstract @NonNull Identifiable<T> createObject(@NonNull T identifier, byte @NonNull[] serialized);
127     }
128
129     private static final long serialVersionUID = 1L;
130
131     private final byte @NonNull [] serialized;
132     private final @NonNull T identifier;
133
134     AbstractIdentifiablePayload(final @NonNull T identifier, final byte @NonNull[] serialized) {
135         this.identifier = requireNonNull(identifier);
136         this.serialized = requireNonNull(serialized);
137     }
138
139     @Override
140     public final T getIdentifier() {
141         return identifier;
142     }
143
144     @Override
145     public final int size() {
146         return serialized.length;
147     }
148
149     protected final byte @NonNull [] serialized() {
150         return serialized;
151     }
152
153     @Override
154     public final int serializedSize() {
155         // TODO: this is not entirely accurate, as the serialization stream has additional overheads:
156         //       - 3 bytes for each block of data <256 bytes
157         //       - 5 bytes for each block of data >=256 bytes
158         //       - each block of data is limited to 1024 bytes as per serialization spec
159         return size() + externalizableProxySize();
160     }
161
162     @Override
163     public final String toString() {
164         return MoreObjects.toStringHelper(this).add("identifier", identifier).add("size", size()).toString();
165     }
166
167     @Override
168     public final Object writeReplace() {
169         return verifyNotNull(externalizableProxy(serialized));
170     }
171
172     protected abstract @NonNull SerialForm externalizableProxy(byte @NonNull[] serialized);
173
174     protected abstract int externalizableProxySize();
175
176     protected static final int externalizableProxySize(final Function<byte[], ? extends SerialForm> constructor) {
177         return SerializationUtils.serialize(constructor.apply(new byte[0])).length;
178     }
179 }