blob: afd69789045ed22b1c4497be4a2565d8115a439e [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070016package org.onlab.util;
17
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070018import com.esotericsoftware.kryo.Kryo;
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -070019import com.esotericsoftware.kryo.Registration;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070020import com.esotericsoftware.kryo.Serializer;
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -070021import com.esotericsoftware.kryo.io.ByteBufferInput;
22import com.esotericsoftware.kryo.io.ByteBufferOutput;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070023import com.esotericsoftware.kryo.io.Input;
Yuta HIGUCHI86f142f2016-07-09 17:44:09 -070024import com.esotericsoftware.kryo.io.Output;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080025import com.esotericsoftware.kryo.pool.KryoCallback;
Yuta HIGUCHI633cf882014-10-20 09:10:28 -070026import com.esotericsoftware.kryo.pool.KryoFactory;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080027import com.esotericsoftware.kryo.pool.KryoPool;
Jordan Haltermanc10a5012017-06-12 10:34:42 -070028import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080029import com.google.common.base.MoreObjects;
Jordan Haltermane1558e82017-06-13 14:38:20 -070030import com.google.common.base.Strings;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070031import com.google.common.collect.ImmutableList;
Jonathan Hartbe093f72016-03-25 11:14:29 -070032import org.apache.commons.lang3.tuple.Pair;
33import org.objenesis.strategy.StdInstantiatorStrategy;
34import org.slf4j.Logger;
35
36import java.io.InputStream;
37import java.io.OutputStream;
38import java.nio.ByteBuffer;
39import java.util.ArrayList;
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -070040import java.util.Arrays;
Jonathan Hartbe093f72016-03-25 11:14:29 -070041import java.util.List;
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -070042import java.util.Objects;
Jonathan Hartbe093f72016-03-25 11:14:29 -070043
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -070044import static com.google.common.base.Preconditions.checkNotNull;
Jonathan Hartbe093f72016-03-25 11:14:29 -070045import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070046
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070047/**
48 * Pool of Kryo instances, with classes pre-registered.
49 */
50//@ThreadSafe
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080051public final class KryoNamespace implements KryoFactory, KryoPool {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070052
53 /**
54 * Default buffer size used for serialization.
55 *
56 * @see #serialize(Object)
57 */
Yuta HIGUCHI38782052014-11-09 23:51:58 -080058 public static final int DEFAULT_BUFFER_SIZE = 4096;
Yuta HIGUCHId2a38822014-11-06 19:05:04 -080059 public static final int MAX_BUFFER_SIZE = 100 * 1000 * 1000;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070060
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080061 /**
62 * ID to use if this KryoNamespace does not define registration id.
63 */
64 public static final int FLOATING_ID = -1;
65
66 /**
67 * Smallest ID free to use for user defined registrations.
68 */
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -070069 public static final int INITIAL_ID = 16;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080070
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -070071 private static final String NO_NAME = "(no name)";
72
HIGUCHI Yutab49b0072016-02-22 22:50:45 -080073 private static final Logger log = getLogger(KryoNamespace.class);
74
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080075 private final KryoPool pool = new KryoPool.Builder(this)
76 .softReferences()
77 .build();
78
79 private final ImmutableList<RegistrationBlock> registeredBlocks;
80
Jordan Haltermane1558e82017-06-13 14:38:20 -070081 private final boolean compatible;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070082 private final boolean registrationRequired;
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -070083 private final String friendlyName;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070084
85 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070086 * KryoNamespace builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070087 */
88 //@NotThreadSafe
89 public static final class Builder {
Jordan Haltermane1558e82017-06-13 14:38:20 -070090 private static final String ISSU_PROPERTY_NAME = "onos.cluster.issu.enabled";
91 private static final boolean DEFAULT_ISSU_ENABLED = false;
92
93 private static final boolean DEFAULT_COMPATIBLE_SERIALIZATION;
94
95 static {
96 String issuEnabled = System.getProperty(ISSU_PROPERTY_NAME);
97 DEFAULT_COMPATIBLE_SERIALIZATION = Strings.isNullOrEmpty(issuEnabled)
98 ? DEFAULT_ISSU_ENABLED : Boolean.parseBoolean(issuEnabled);
99 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700100
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800101 private int blockHeadId = INITIAL_ID;
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700102 private List<Pair<Class<?>[], Serializer<?>>> types = new ArrayList<>();
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800103 private List<RegistrationBlock> blocks = new ArrayList<>();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700104 private boolean registrationRequired = true;
Jordan Haltermane1558e82017-06-13 14:38:20 -0700105 private boolean compatible = DEFAULT_COMPATIBLE_SERIALIZATION;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700106
107 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700108 * Builds a {@link KryoNamespace} instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700109 *
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700110 * @return KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700111 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700112 public KryoNamespace build() {
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700113 return build(NO_NAME);
114 }
115
116 /**
117 * Builds a {@link KryoNamespace} instance.
118 *
119 * @param friendlyName friendly name for the namespace
120 * @return KryoNamespace
121 */
122 public KryoNamespace build(String friendlyName) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800123 if (!types.isEmpty()) {
124 blocks.add(new RegistrationBlock(this.blockHeadId, types));
125 }
Jordan Haltermane1558e82017-06-13 14:38:20 -0700126 return new KryoNamespace(blocks, registrationRequired, compatible, friendlyName).populate(1);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800127 }
128
129 /**
130 * Sets the next Kryo registration Id for following register entries.
131 *
132 * @param id Kryo registration Id
133 * @return this
134 *
135 * @see Kryo#register(Class, Serializer, int)
136 */
137 public Builder nextId(final int id) {
138 if (!types.isEmpty()) {
HIGUCHI Yutab49b0072016-02-22 22:50:45 -0800139 if (id != FLOATING_ID && id < blockHeadId + types.size()) {
140
HIGUCHI Yuta163efb52016-05-18 19:24:19 -0700141 if (log.isWarnEnabled()) {
142 log.warn("requested nextId {} could potentially overlap " +
143 "with existing registrations {}+{} ",
144 id, blockHeadId, types.size(), new RuntimeException());
145 }
HIGUCHI Yutab49b0072016-02-22 22:50:45 -0800146 }
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800147 blocks.add(new RegistrationBlock(this.blockHeadId, types));
148 types = new ArrayList<>();
149 }
150 this.blockHeadId = id;
151 return this;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700152 }
153
154 /**
155 * Registers classes to be serialized using Kryo default serializer.
156 *
157 * @param expectedTypes list of classes
158 * @return this
159 */
160 public Builder register(final Class<?>... expectedTypes) {
161 for (Class<?> clazz : expectedTypes) {
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700162 types.add(Pair.of(new Class<?>[]{clazz}, null));
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700163 }
164 return this;
165 }
166
167 /**
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700168 * Registers serializer for the given set of classes.
169 * <p>
170 * When multiple classes are registered with an explicitly provided serializer, the namespace guarantees
171 * all instances will be serialized with the same type ID.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700172 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800173 * @param classes list of classes to register
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700174 * @param serializer serializer to use for the class
175 * @return this
176 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800177 public Builder register(Serializer<?> serializer, final Class<?>... classes) {
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700178 types.add(Pair.of(classes, checkNotNull(serializer)));
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800179 return this;
180 }
181
182 private Builder register(RegistrationBlock block) {
183 if (block.begin() != FLOATING_ID) {
184 // flush pending types
185 nextId(block.begin());
186 blocks.add(block);
187 nextId(block.begin() + block.types().size());
188 } else {
189 // flush pending types
190 final int addedBlockBegin = blockHeadId + types.size();
191 nextId(addedBlockBegin);
192 blocks.add(new RegistrationBlock(addedBlockBegin, block.types()));
193 nextId(addedBlockBegin + block.types().size());
194 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700195 return this;
196 }
197
198 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700199 * Registers all the class registered to given KryoNamespace.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700200 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800201 * @param ns KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700202 * @return this
203 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800204 public Builder register(final KryoNamespace ns) {
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700205
206 if (blocks.containsAll(ns.registeredBlocks)) {
207 // Everything was already registered.
208 log.debug("Ignoring {}, already registered.", ns);
209 return this;
210 }
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800211 for (RegistrationBlock block : ns.registeredBlocks) {
212 this.register(block);
213 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700214 return this;
215 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700216
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800217 /**
Jordan Haltermane1558e82017-06-13 14:38:20 -0700218 * Sets whether backwards/forwards compatible versioned serialization is enabled.
219 * <p>
220 * When compatible serialization is enabled, the {@link CompatibleFieldSerializer} will be set as the
221 * default serializer for types that do not otherwise explicitly specify a serializer.
222 *
223 * @param compatible whether versioned serialization is enabled
224 * @return this
225 */
226 public Builder setCompatible(boolean compatible) {
227 this.compatible = compatible;
228 return this;
229 }
230
231 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800232 * Sets the registrationRequired flag.
233 *
234 * @param registrationRequired Kryo's registrationRequired flag
235 * @return this
236 *
237 * @see Kryo#setRegistrationRequired(boolean)
238 */
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700239 public Builder setRegistrationRequired(boolean registrationRequired) {
240 this.registrationRequired = registrationRequired;
241 return this;
242 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700243 }
244
245 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700246 * Creates a new {@link KryoNamespace} builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700247 *
248 * @return builder
249 */
250 public static Builder newBuilder() {
251 return new Builder();
252 }
253
254 /**
255 * Creates a Kryo instance pool.
256 *
Jonathan Hart4f60f982014-10-27 08:11:17 -0700257 * @param registeredTypes types to register
Jordan Haltermane1558e82017-06-13 14:38:20 -0700258 * @param registrationRequired whether registration is required
259 * @param compatible whether compatible serialization is enabled
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700260 * @param friendlyName friendly name for the namespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700261 */
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700262 private KryoNamespace(final List<RegistrationBlock> registeredTypes,
263 boolean registrationRequired,
Jordan Haltermane1558e82017-06-13 14:38:20 -0700264 boolean compatible,
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700265 String friendlyName) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800266 this.registeredBlocks = ImmutableList.copyOf(registeredTypes);
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700267 this.registrationRequired = registrationRequired;
Jordan Haltermane1558e82017-06-13 14:38:20 -0700268 this.compatible = compatible;
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700269 this.friendlyName = checkNotNull(friendlyName);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700270 }
271
272 /**
273 * Populates the Kryo pool.
274 *
275 * @param instances to add to the pool
276 * @return this
277 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700278 public KryoNamespace populate(int instances) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800279
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700280 for (int i = 0; i < instances; ++i) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800281 release(create());
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700282 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700283 return this;
284 }
285
286 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700287 * Serializes given object to byte array using Kryo instance in pool.
288 * <p>
Yuta HIGUCHI38782052014-11-09 23:51:58 -0800289 * Note: Serialized bytes must be smaller than {@link #MAX_BUFFER_SIZE}.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700290 *
291 * @param obj Object to serialize
292 * @return serialized bytes
293 */
294 public byte[] serialize(final Object obj) {
295 return serialize(obj, DEFAULT_BUFFER_SIZE);
296 }
297
298 /**
299 * Serializes given object to byte array using Kryo instance in pool.
300 *
301 * @param obj Object to serialize
302 * @param bufferSize maximum size of serialized bytes
303 * @return serialized bytes
304 */
305 public byte[] serialize(final Object obj, final int bufferSize) {
Yuta HIGUCHI86f142f2016-07-09 17:44:09 -0700306 Output out = new Output(bufferSize, MAX_BUFFER_SIZE);
307 return pool.run(kryo -> {
308 kryo.writeClassAndObject(out, obj);
309 out.flush();
310 return out.toBytes();
311 });
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700312 }
313
314 /**
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700315 * Serializes given object to byte buffer using Kryo instance in pool.
316 *
317 * @param obj Object to serialize
318 * @param buffer to write to
319 */
320 public void serialize(final Object obj, final ByteBuffer buffer) {
321 ByteBufferOutput out = new ByteBufferOutput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800322 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700323 try {
324 kryo.writeClassAndObject(out, obj);
Yuta HIGUCHIcac919c2014-10-20 22:17:20 -0700325 out.flush();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700326 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800327 release(kryo);
328 }
329 }
330
331 /**
332 * Serializes given object to OutputStream using Kryo instance in pool.
333 *
334 * @param obj Object to serialize
335 * @param stream to write to
336 */
337 public void serialize(final Object obj, final OutputStream stream) {
338 serialize(obj, stream, DEFAULT_BUFFER_SIZE);
339 }
340
341 /**
342 * Serializes given object to OutputStream using Kryo instance in pool.
343 *
344 * @param obj Object to serialize
345 * @param stream to write to
346 * @param bufferSize size of the buffer in front of the stream
347 */
348 public void serialize(final Object obj, final OutputStream stream, final int bufferSize) {
349 ByteBufferOutput out = new ByteBufferOutput(stream, bufferSize);
350 Kryo kryo = borrow();
351 try {
352 kryo.writeClassAndObject(out, obj);
353 out.flush();
354 } finally {
355 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700356 }
357 }
358
359 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700360 * Deserializes given byte array to Object using Kryo instance in pool.
361 *
362 * @param bytes serialized bytes
363 * @param <T> deserialized Object type
364 * @return deserialized Object
365 */
366 public <T> T deserialize(final byte[] bytes) {
367 Input in = new Input(bytes);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800368 Kryo kryo = borrow();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700369 try {
370 @SuppressWarnings("unchecked")
371 T obj = (T) kryo.readClassAndObject(in);
372 return obj;
373 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800374 release(kryo);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700375 }
376 }
377
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700378 /**
379 * Deserializes given byte buffer to Object using Kryo instance in pool.
380 *
381 * @param buffer input with serialized bytes
382 * @param <T> deserialized Object type
383 * @return deserialized Object
384 */
385 public <T> T deserialize(final ByteBuffer buffer) {
386 ByteBufferInput in = new ByteBufferInput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800387 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700388 try {
389 @SuppressWarnings("unchecked")
390 T obj = (T) kryo.readClassAndObject(in);
391 return obj;
392 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800393 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700394 }
395 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700396
397 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800398 * Deserializes given InputStream to an Object using Kryo instance in pool.
399 *
400 * @param stream input stream
401 * @param <T> deserialized Object type
402 * @return deserialized Object
403 */
404 public <T> T deserialize(final InputStream stream) {
405 return deserialize(stream, DEFAULT_BUFFER_SIZE);
406 }
407
408 /**
409 * Deserializes given InputStream to an Object using Kryo instance in pool.
410 *
411 * @param stream input stream
412 * @param <T> deserialized Object type
413 * @return deserialized Object
414 * @param bufferSize size of the buffer in front of the stream
415 */
416 public <T> T deserialize(final InputStream stream, final int bufferSize) {
417 ByteBufferInput in = new ByteBufferInput(stream, bufferSize);
418 Kryo kryo = borrow();
419 try {
420 @SuppressWarnings("unchecked")
421 T obj = (T) kryo.readClassAndObject(in);
422 return obj;
423 } finally {
424 release(kryo);
425 }
426 }
427
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700428 private String friendlyName() {
429 return friendlyName;
430 }
431
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800432 /**
Jonathan Hart679d24d2016-05-13 13:29:53 -0700433 * Gets the number of classes registered in this Kryo namespace.
434 *
435 * @return size of namespace
436 */
437 public int size() {
438 return (int) registeredBlocks.stream()
439 .flatMap(block -> block.types().stream())
440 .count();
441 }
442
443 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800444 * Creates a Kryo instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700445 *
446 * @return Kryo instance
447 */
Yuta HIGUCHI633cf882014-10-20 09:10:28 -0700448 @Override
449 public Kryo create() {
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700450 log.trace("Creating Kryo instance for {}", this);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700451 Kryo kryo = new Kryo();
452 kryo.setRegistrationRequired(registrationRequired);
Jordan Haltermane1558e82017-06-13 14:38:20 -0700453
454 // If compatible serialization is enabled, override the default serializer.
455 if (compatible) {
456 kryo.setDefaultSerializer(CompatibleFieldSerializer::new);
457 }
Jonathan Hartbe093f72016-03-25 11:14:29 -0700458
459 // TODO rethink whether we want to use StdInstantiatorStrategy
460 kryo.setInstantiatorStrategy(
461 new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
462
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800463 for (RegistrationBlock block : registeredBlocks) {
464 int id = block.begin();
465 if (id == FLOATING_ID) {
466 id = kryo.getNextRegistrationId();
467 }
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700468 for (Pair<Class<?>[], Serializer<?>> entry : block.types()) {
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700469 register(kryo, entry.getLeft(), entry.getRight(), id++);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700470 }
471 }
472 return kryo;
473 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700474
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700475 /**
476 * Register {@code type} and {@code serializer} to {@code kryo} instance.
477 *
478 * @param kryo Kryo instance
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700479 * @param types types to register
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700480 * @param serializer Specific serializer to register or null to use default.
481 * @param id type registration id to use
482 */
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700483 private void register(Kryo kryo, Class<?>[] types, Serializer<?> serializer, int id) {
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700484 Registration existing = kryo.getRegistration(id);
485 if (existing != null) {
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700486 boolean matches = false;
487 for (Class<?> type : types) {
488 if (existing.getType() == type) {
489 matches = true;
490 break;
491 }
492 }
493
494 if (!matches) {
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700495 log.error("{}: Failed to register {} as {}, {} was already registered.",
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700496 friendlyName(), types, id, existing.getType());
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700497
498 throw new IllegalStateException(String.format(
499 "Failed to register %s as %s, %s was already registered.",
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700500 Arrays.toString(types), id, existing.getType()));
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700501 }
502 // falling through to register call for now.
503 // Consider skipping, if there's reasonable
504 // way to compare serializer equivalence.
505 }
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700506
507 for (Class<?> type : types) {
508 Registration r;
509 if (serializer == null) {
510 r = kryo.register(type, id);
511 } else {
512 r = kryo.register(type, serializer, id);
513 }
514 if (r.getId() != id) {
Ray Milkey5dae9222017-06-15 11:55:51 -0700515 log.warn("{}: {} already registered as {}. Skipping {}.",
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700516 friendlyName(), r.getType(), r.getId(), id);
517 }
518 log.trace("{} registered as {}", r.getType(), r.getId());
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700519 }
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700520 }
521
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800522 @Override
523 public Kryo borrow() {
524 return pool.borrow();
525 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700526
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800527 @Override
528 public void release(Kryo kryo) {
529 pool.release(kryo);
530 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700531
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800532 @Override
533 public <T> T run(KryoCallback<T> callback) {
534 return pool.run(callback);
535 }
536
537 @Override
538 public String toString() {
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700539 if (friendlyName != NO_NAME) {
540 return MoreObjects.toStringHelper(getClass())
541 .omitNullValues()
542 .add("friendlyName", friendlyName)
543 // omit lengthy detail, when there's a name
544 .toString();
545 }
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800546 return MoreObjects.toStringHelper(getClass())
547 .add("registeredBlocks", registeredBlocks)
548 .toString();
549 }
550
551 static final class RegistrationBlock {
552 private final int begin;
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700553 private final ImmutableList<Pair<Class<?>[], Serializer<?>>> types;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800554
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700555 public RegistrationBlock(int begin, List<Pair<Class<?>[], Serializer<?>>> types) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800556 this.begin = begin;
557 this.types = ImmutableList.copyOf(types);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700558 }
559
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800560 public int begin() {
561 return begin;
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700562 }
563
Jordan Halterman0d1f3fa2017-05-26 00:14:27 -0700564 public ImmutableList<Pair<Class<?>[], Serializer<?>>> types() {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800565 return types;
566 }
567
568 @Override
569 public String toString() {
570 return MoreObjects.toStringHelper(getClass())
571 .add("begin", begin)
572 .add("types", types)
573 .toString();
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700574 }
HIGUCHI Yutab2d0fd82016-05-17 20:34:58 -0700575
576 @Override
577 public int hashCode() {
578 return types.hashCode();
579 }
580
581 // Only the registered types are used for equality.
582 @Override
583 public boolean equals(Object obj) {
584 if (this == obj) {
585 return true;
586 }
587
588 if (obj instanceof RegistrationBlock) {
589 RegistrationBlock that = (RegistrationBlock) obj;
590 return Objects.equals(this.types, that.types);
591 }
592 return false;
593 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700594 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700595}