blob: b5c3926bc8f5abdf34b9f458124baa48f2621dca [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 HIGUCHI91768e32014-11-22 05:06:35 -080024import com.esotericsoftware.kryo.pool.KryoCallback;
Yuta HIGUCHI633cf882014-10-20 09:10:28 -070025import com.esotericsoftware.kryo.pool.KryoFactory;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080026import com.esotericsoftware.kryo.pool.KryoPool;
27import com.google.common.base.MoreObjects;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070028import com.google.common.collect.ImmutableList;
Jonathan Hartbe093f72016-03-25 11:14:29 -070029import org.apache.commons.lang3.tuple.Pair;
30import org.objenesis.strategy.StdInstantiatorStrategy;
31import org.slf4j.Logger;
32
33import java.io.InputStream;
34import java.io.OutputStream;
35import java.nio.ByteBuffer;
36import java.util.ArrayList;
37import java.util.List;
38
39import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070040
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070041/**
42 * Pool of Kryo instances, with classes pre-registered.
43 */
44//@ThreadSafe
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080045public final class KryoNamespace implements KryoFactory, KryoPool {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070046
47 /**
48 * Default buffer size used for serialization.
49 *
50 * @see #serialize(Object)
51 */
Yuta HIGUCHI38782052014-11-09 23:51:58 -080052 public static final int DEFAULT_BUFFER_SIZE = 4096;
Yuta HIGUCHId2a38822014-11-06 19:05:04 -080053 public static final int MAX_BUFFER_SIZE = 100 * 1000 * 1000;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070054
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080055 /**
56 * ID to use if this KryoNamespace does not define registration id.
57 */
58 public static final int FLOATING_ID = -1;
59
60 /**
61 * Smallest ID free to use for user defined registrations.
62 */
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -070063 public static final int INITIAL_ID = 16;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080064
HIGUCHI Yutab49b0072016-02-22 22:50:45 -080065 private static final Logger log = getLogger(KryoNamespace.class);
66
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080067
68 private final KryoPool pool = new KryoPool.Builder(this)
69 .softReferences()
70 .build();
71
72 private final ImmutableList<RegistrationBlock> registeredBlocks;
73
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070074 private final boolean registrationRequired;
75
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080076
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070077 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070078 * KryoNamespace builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070079 */
80 //@NotThreadSafe
81 public static final class Builder {
82
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080083 private int blockHeadId = INITIAL_ID;
84 private List<Pair<Class<?>, Serializer<?>>> types = new ArrayList<>();
85 private List<RegistrationBlock> blocks = new ArrayList<>();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070086 private boolean registrationRequired = true;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070087
88 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070089 * Builds a {@link KryoNamespace} instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070090 *
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070091 * @return KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070092 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070093 public KryoNamespace build() {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080094 if (!types.isEmpty()) {
95 blocks.add(new RegistrationBlock(this.blockHeadId, types));
96 }
97 return new KryoNamespace(blocks, registrationRequired).populate(1);
98 }
99
100 /**
101 * Sets the next Kryo registration Id for following register entries.
102 *
103 * @param id Kryo registration Id
104 * @return this
105 *
106 * @see Kryo#register(Class, Serializer, int)
107 */
108 public Builder nextId(final int id) {
109 if (!types.isEmpty()) {
HIGUCHI Yutab49b0072016-02-22 22:50:45 -0800110 if (id != FLOATING_ID && id < blockHeadId + types.size()) {
111
112 log.warn("requested nextId {} could potentially overlap" +
113 "with existing registrations {}+{} ",
114 id, blockHeadId, types.size());
115 }
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800116 blocks.add(new RegistrationBlock(this.blockHeadId, types));
117 types = new ArrayList<>();
118 }
119 this.blockHeadId = id;
120 return this;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700121 }
122
123 /**
124 * Registers classes to be serialized using Kryo default serializer.
125 *
126 * @param expectedTypes list of classes
127 * @return this
128 */
129 public Builder register(final Class<?>... expectedTypes) {
130 for (Class<?> clazz : expectedTypes) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800131 types.add(Pair.of(clazz, null));
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700132 }
133 return this;
134 }
135
136 /**
137 * Registers a class and it's serializer.
138 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800139 * @param classes list of classes to register
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700140 * @param serializer serializer to use for the class
141 * @return this
142 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800143 public Builder register(Serializer<?> serializer, final Class<?>... classes) {
144 for (Class<?> clazz : classes) {
145 types.add(Pair.of(clazz, serializer));
146 }
147 return this;
148 }
149
150 private Builder register(RegistrationBlock block) {
151 if (block.begin() != FLOATING_ID) {
152 // flush pending types
153 nextId(block.begin());
154 blocks.add(block);
155 nextId(block.begin() + block.types().size());
156 } else {
157 // flush pending types
158 final int addedBlockBegin = blockHeadId + types.size();
159 nextId(addedBlockBegin);
160 blocks.add(new RegistrationBlock(addedBlockBegin, block.types()));
161 nextId(addedBlockBegin + block.types().size());
162 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700163 return this;
164 }
165
166 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700167 * Registers all the class registered to given KryoNamespace.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700168 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800169 * @param ns KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700170 * @return this
171 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800172 public Builder register(final KryoNamespace ns) {
173 for (RegistrationBlock block : ns.registeredBlocks) {
174 this.register(block);
175 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700176 return this;
177 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700178
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800179 /**
180 * Sets the registrationRequired flag.
181 *
182 * @param registrationRequired Kryo's registrationRequired flag
183 * @return this
184 *
185 * @see Kryo#setRegistrationRequired(boolean)
186 */
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700187 public Builder setRegistrationRequired(boolean registrationRequired) {
188 this.registrationRequired = registrationRequired;
189 return this;
190 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700191 }
192
193 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700194 * Creates a new {@link KryoNamespace} builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700195 *
196 * @return builder
197 */
198 public static Builder newBuilder() {
199 return new Builder();
200 }
201
202 /**
203 * Creates a Kryo instance pool.
204 *
Jonathan Hart4f60f982014-10-27 08:11:17 -0700205 * @param registeredTypes types to register
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700206 * @param registrationRequired
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700207 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800208 private KryoNamespace(final List<RegistrationBlock> registeredTypes, boolean registrationRequired) {
209 this.registeredBlocks = ImmutableList.copyOf(registeredTypes);
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700210 this.registrationRequired = registrationRequired;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700211 }
212
213 /**
214 * Populates the Kryo pool.
215 *
216 * @param instances to add to the pool
217 * @return this
218 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700219 public KryoNamespace populate(int instances) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800220
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700221 for (int i = 0; i < instances; ++i) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800222 release(create());
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700223 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700224 return this;
225 }
226
227 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700228 * Serializes given object to byte array using Kryo instance in pool.
229 * <p>
Yuta HIGUCHI38782052014-11-09 23:51:58 -0800230 * Note: Serialized bytes must be smaller than {@link #MAX_BUFFER_SIZE}.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700231 *
232 * @param obj Object to serialize
233 * @return serialized bytes
234 */
235 public byte[] serialize(final Object obj) {
236 return serialize(obj, DEFAULT_BUFFER_SIZE);
237 }
238
239 /**
240 * Serializes given object to byte array using Kryo instance in pool.
241 *
242 * @param obj Object to serialize
243 * @param bufferSize maximum size of serialized bytes
244 * @return serialized bytes
245 */
246 public byte[] serialize(final Object obj, final int bufferSize) {
Yuta HIGUCHId2a38822014-11-06 19:05:04 -0800247 ByteBufferOutput out = new ByteBufferOutput(bufferSize, MAX_BUFFER_SIZE);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700248 try {
Madan Jampani22fa5cb2015-04-13 15:53:44 -0700249 Kryo kryo = borrow();
250 try {
251 kryo.writeClassAndObject(out, obj);
252 out.flush();
253 return out.toBytes();
254 } finally {
255 release(kryo);
256 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700257 } finally {
Madan Jampani22fa5cb2015-04-13 15:53:44 -0700258 out.release();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700259 }
260 }
261
262 /**
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700263 * Serializes given object to byte buffer using Kryo instance in pool.
264 *
265 * @param obj Object to serialize
266 * @param buffer to write to
267 */
268 public void serialize(final Object obj, final ByteBuffer buffer) {
269 ByteBufferOutput out = new ByteBufferOutput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800270 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700271 try {
272 kryo.writeClassAndObject(out, obj);
Yuta HIGUCHIcac919c2014-10-20 22:17:20 -0700273 out.flush();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700274 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800275 release(kryo);
276 }
277 }
278
279 /**
280 * Serializes given object to OutputStream using Kryo instance in pool.
281 *
282 * @param obj Object to serialize
283 * @param stream to write to
284 */
285 public void serialize(final Object obj, final OutputStream stream) {
286 serialize(obj, stream, DEFAULT_BUFFER_SIZE);
287 }
288
289 /**
290 * Serializes given object to OutputStream using Kryo instance in pool.
291 *
292 * @param obj Object to serialize
293 * @param stream to write to
294 * @param bufferSize size of the buffer in front of the stream
295 */
296 public void serialize(final Object obj, final OutputStream stream, final int bufferSize) {
297 ByteBufferOutput out = new ByteBufferOutput(stream, bufferSize);
298 Kryo kryo = borrow();
299 try {
300 kryo.writeClassAndObject(out, obj);
301 out.flush();
302 } finally {
303 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700304 }
305 }
306
307 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700308 * Deserializes given byte array to Object using Kryo instance in pool.
309 *
310 * @param bytes serialized bytes
311 * @param <T> deserialized Object type
312 * @return deserialized Object
313 */
314 public <T> T deserialize(final byte[] bytes) {
315 Input in = new Input(bytes);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800316 Kryo kryo = borrow();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700317 try {
318 @SuppressWarnings("unchecked")
319 T obj = (T) kryo.readClassAndObject(in);
320 return obj;
321 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800322 release(kryo);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700323 }
324 }
325
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700326 /**
327 * Deserializes given byte buffer to Object using Kryo instance in pool.
328 *
329 * @param buffer input with serialized bytes
330 * @param <T> deserialized Object type
331 * @return deserialized Object
332 */
333 public <T> T deserialize(final ByteBuffer buffer) {
334 ByteBufferInput in = new ByteBufferInput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800335 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700336 try {
337 @SuppressWarnings("unchecked")
338 T obj = (T) kryo.readClassAndObject(in);
339 return obj;
340 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800341 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700342 }
343 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700344
345 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800346 * Deserializes given InputStream to an Object using Kryo instance in pool.
347 *
348 * @param stream input stream
349 * @param <T> deserialized Object type
350 * @return deserialized Object
351 */
352 public <T> T deserialize(final InputStream stream) {
353 return deserialize(stream, DEFAULT_BUFFER_SIZE);
354 }
355
356 /**
357 * Deserializes given InputStream to an Object using Kryo instance in pool.
358 *
359 * @param stream input stream
360 * @param <T> deserialized Object type
361 * @return deserialized Object
362 * @param bufferSize size of the buffer in front of the stream
363 */
364 public <T> T deserialize(final InputStream stream, final int bufferSize) {
365 ByteBufferInput in = new ByteBufferInput(stream, bufferSize);
366 Kryo kryo = borrow();
367 try {
368 @SuppressWarnings("unchecked")
369 T obj = (T) kryo.readClassAndObject(in);
370 return obj;
371 } finally {
372 release(kryo);
373 }
374 }
375
376 /**
377 * Creates a Kryo instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700378 *
379 * @return Kryo instance
380 */
Yuta HIGUCHI633cf882014-10-20 09:10:28 -0700381 @Override
382 public Kryo create() {
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700383 log.trace("Creating Kryo instance for {}", this);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700384 Kryo kryo = new Kryo();
385 kryo.setRegistrationRequired(registrationRequired);
Jonathan Hartbe093f72016-03-25 11:14:29 -0700386
387 // TODO rethink whether we want to use StdInstantiatorStrategy
388 kryo.setInstantiatorStrategy(
389 new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
390
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800391 for (RegistrationBlock block : registeredBlocks) {
392 int id = block.begin();
393 if (id == FLOATING_ID) {
394 id = kryo.getNextRegistrationId();
395 }
396 for (Pair<Class<?>, Serializer<?>> entry : block.types()) {
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700397 register(kryo, entry.getLeft(), entry.getRight(), id++);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700398 }
399 }
400 return kryo;
401 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700402
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700403 /**
404 * Register {@code type} and {@code serializer} to {@code kryo} instance.
405 *
406 * @param kryo Kryo instance
407 * @param type type to register
408 * @param serializer Specific serializer to register or null to use default.
409 * @param id type registration id to use
410 */
411 private static void register(Kryo kryo, Class<?> type, Serializer<?> serializer, int id) {
412 Registration existing = kryo.getRegistration(id);
413 if (existing != null) {
414 if (existing.getType() != type) {
415 log.error("Failed to register {} as {}, {} was already registered.",
416 type, id, existing.getType());
417
418 throw new IllegalStateException(String.format(
419 "Failed to register %s as %s, %s was already registered.",
420 type, id, existing.getType()));
421 }
422 // falling through to register call for now.
423 // Consider skipping, if there's reasonable
424 // way to compare serializer equivalence.
425 }
426 Registration r;
427 if (serializer == null) {
428 r = kryo.register(type, id);
429 } else {
430 r = kryo.register(type, serializer, id);
431 }
432 if (r.getId() != id) {
Ray Milkey6f955682016-05-13 13:14:30 -0700433 log.info("{} already registed as {}. Skipping {}.",
HIGUCHI Yuta0a1f29e2016-05-05 15:34:41 -0700434 r.getType(), r.getId(), id);
435 }
436 log.trace("{} registered as {}", r.getType(), r.getId());
437 }
438
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800439 @Override
440 public Kryo borrow() {
441 return pool.borrow();
442 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700443
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800444 @Override
445 public void release(Kryo kryo) {
446 pool.release(kryo);
447 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700448
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800449 @Override
450 public <T> T run(KryoCallback<T> callback) {
451 return pool.run(callback);
452 }
453
454 @Override
455 public String toString() {
456 return MoreObjects.toStringHelper(getClass())
457 .add("registeredBlocks", registeredBlocks)
458 .toString();
459 }
460
461 static final class RegistrationBlock {
462 private final int begin;
463 private final ImmutableList<Pair<Class<?>, Serializer<?>>> types;
464
465 public RegistrationBlock(int begin, List<Pair<Class<?>, Serializer<?>>> types) {
466 this.begin = begin;
467 this.types = ImmutableList.copyOf(types);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700468 }
469
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800470 public int begin() {
471 return begin;
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700472 }
473
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800474 public ImmutableList<Pair<Class<?>, Serializer<?>>> types() {
475 return types;
476 }
477
478 @Override
479 public String toString() {
480 return MoreObjects.toStringHelper(getClass())
481 .add("begin", begin)
482 .add("types", types)
483 .toString();
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700484 }
485 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700486}