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