blob: f5d2f9b1b6d9b118e90a9ff584902fa30f479a8a [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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
Jonathan Harte65a8f62016-03-25 19:19:41 +000018import static org.slf4j.LoggerFactory.getLogger;
19
20import java.io.InputStream;
21import java.io.OutputStream;
22import java.nio.ByteBuffer;
23import java.util.ArrayList;
24import java.util.List;
25
26import org.apache.commons.lang3.tuple.Pair;
27import org.slf4j.Logger;
28
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070029import com.esotericsoftware.kryo.Kryo;
30import com.esotericsoftware.kryo.Serializer;
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -070031import com.esotericsoftware.kryo.io.ByteBufferInput;
32import com.esotericsoftware.kryo.io.ByteBufferOutput;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070033import com.esotericsoftware.kryo.io.Input;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080034import com.esotericsoftware.kryo.pool.KryoCallback;
Yuta HIGUCHI633cf882014-10-20 09:10:28 -070035import com.esotericsoftware.kryo.pool.KryoFactory;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080036import com.esotericsoftware.kryo.pool.KryoPool;
37import com.google.common.base.MoreObjects;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070038import com.google.common.collect.ImmutableList;
39
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070040/**
41 * Pool of Kryo instances, with classes pre-registered.
42 */
43//@ThreadSafe
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080044public final class KryoNamespace implements KryoFactory, KryoPool {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070045
46 /**
47 * Default buffer size used for serialization.
48 *
49 * @see #serialize(Object)
50 */
Yuta HIGUCHI38782052014-11-09 23:51:58 -080051 public static final int DEFAULT_BUFFER_SIZE = 4096;
Yuta HIGUCHId2a38822014-11-06 19:05:04 -080052 public static final int MAX_BUFFER_SIZE = 100 * 1000 * 1000;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070053
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080054 /**
55 * ID to use if this KryoNamespace does not define registration id.
56 */
57 public static final int FLOATING_ID = -1;
58
59 /**
60 * Smallest ID free to use for user defined registrations.
61 */
62 public static final int INITIAL_ID = 11;
63
HIGUCHI Yutab49b0072016-02-22 22:50:45 -080064 private static final Logger log = getLogger(KryoNamespace.class);
65
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080066
67 private final KryoPool pool = new KryoPool.Builder(this)
68 .softReferences()
69 .build();
70
71 private final ImmutableList<RegistrationBlock> registeredBlocks;
72
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070073 private final boolean registrationRequired;
74
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080075
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070076 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070077 * KryoNamespace builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070078 */
79 //@NotThreadSafe
80 public static final class Builder {
81
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080082 private int blockHeadId = INITIAL_ID;
83 private List<Pair<Class<?>, Serializer<?>>> types = new ArrayList<>();
84 private List<RegistrationBlock> blocks = new ArrayList<>();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070085 private boolean registrationRequired = true;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070086
87 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070088 * Builds a {@link KryoNamespace} instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070089 *
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070090 * @return KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070091 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070092 public KryoNamespace build() {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080093 if (!types.isEmpty()) {
94 blocks.add(new RegistrationBlock(this.blockHeadId, types));
95 }
96 return new KryoNamespace(blocks, registrationRequired).populate(1);
97 }
98
99 /**
100 * Sets the next Kryo registration Id for following register entries.
101 *
102 * @param id Kryo registration Id
103 * @return this
104 *
105 * @see Kryo#register(Class, Serializer, int)
106 */
107 public Builder nextId(final int id) {
108 if (!types.isEmpty()) {
HIGUCHI Yutab49b0072016-02-22 22:50:45 -0800109 if (id != FLOATING_ID && id < blockHeadId + types.size()) {
110
111 log.warn("requested nextId {} could potentially overlap" +
112 "with existing registrations {}+{} ",
113 id, blockHeadId, types.size());
114 }
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800115 blocks.add(new RegistrationBlock(this.blockHeadId, types));
116 types = new ArrayList<>();
117 }
118 this.blockHeadId = id;
119 return this;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700120 }
121
122 /**
123 * Registers classes to be serialized using Kryo default serializer.
124 *
125 * @param expectedTypes list of classes
126 * @return this
127 */
128 public Builder register(final Class<?>... expectedTypes) {
129 for (Class<?> clazz : expectedTypes) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800130 types.add(Pair.of(clazz, null));
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700131 }
132 return this;
133 }
134
135 /**
136 * Registers a class and it's serializer.
137 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800138 * @param classes list of classes to register
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700139 * @param serializer serializer to use for the class
140 * @return this
141 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800142 public Builder register(Serializer<?> serializer, final Class<?>... classes) {
143 for (Class<?> clazz : classes) {
144 types.add(Pair.of(clazz, serializer));
145 }
146 return this;
147 }
148
149 private Builder register(RegistrationBlock block) {
150 if (block.begin() != FLOATING_ID) {
151 // flush pending types
152 nextId(block.begin());
153 blocks.add(block);
154 nextId(block.begin() + block.types().size());
155 } else {
156 // flush pending types
157 final int addedBlockBegin = blockHeadId + types.size();
158 nextId(addedBlockBegin);
159 blocks.add(new RegistrationBlock(addedBlockBegin, block.types()));
160 nextId(addedBlockBegin + block.types().size());
161 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700162 return this;
163 }
164
165 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700166 * Registers all the class registered to given KryoNamespace.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700167 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800168 * @param ns KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700169 * @return this
170 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800171 public Builder register(final KryoNamespace ns) {
172 for (RegistrationBlock block : ns.registeredBlocks) {
173 this.register(block);
174 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700175 return this;
176 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700177
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800178 /**
179 * Sets the registrationRequired flag.
180 *
181 * @param registrationRequired Kryo's registrationRequired flag
182 * @return this
183 *
184 * @see Kryo#setRegistrationRequired(boolean)
185 */
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700186 public Builder setRegistrationRequired(boolean registrationRequired) {
187 this.registrationRequired = registrationRequired;
188 return this;
189 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700190 }
191
192 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700193 * Creates a new {@link KryoNamespace} builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700194 *
195 * @return builder
196 */
197 public static Builder newBuilder() {
198 return new Builder();
199 }
200
201 /**
202 * Creates a Kryo instance pool.
203 *
Jonathan Hart4f60f982014-10-27 08:11:17 -0700204 * @param registeredTypes types to register
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700205 * @param registrationRequired
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700206 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800207 private KryoNamespace(final List<RegistrationBlock> registeredTypes, boolean registrationRequired) {
208 this.registeredBlocks = ImmutableList.copyOf(registeredTypes);
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700209 this.registrationRequired = registrationRequired;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700210 }
211
212 /**
213 * Populates the Kryo pool.
214 *
215 * @param instances to add to the pool
216 * @return this
217 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700218 public KryoNamespace populate(int instances) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800219
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700220 for (int i = 0; i < instances; ++i) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800221 release(create());
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700222 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700223 return this;
224 }
225
226 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700227 * Serializes given object to byte array using Kryo instance in pool.
228 * <p>
Yuta HIGUCHI38782052014-11-09 23:51:58 -0800229 * Note: Serialized bytes must be smaller than {@link #MAX_BUFFER_SIZE}.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700230 *
231 * @param obj Object to serialize
232 * @return serialized bytes
233 */
234 public byte[] serialize(final Object obj) {
235 return serialize(obj, DEFAULT_BUFFER_SIZE);
236 }
237
238 /**
239 * Serializes given object to byte array using Kryo instance in pool.
240 *
241 * @param obj Object to serialize
242 * @param bufferSize maximum size of serialized bytes
243 * @return serialized bytes
244 */
245 public byte[] serialize(final Object obj, final int bufferSize) {
Yuta HIGUCHId2a38822014-11-06 19:05:04 -0800246 ByteBufferOutput out = new ByteBufferOutput(bufferSize, MAX_BUFFER_SIZE);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700247 try {
Madan Jampani22fa5cb2015-04-13 15:53:44 -0700248 Kryo kryo = borrow();
249 try {
250 kryo.writeClassAndObject(out, obj);
251 out.flush();
252 return out.toBytes();
253 } finally {
254 release(kryo);
255 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700256 } finally {
Madan Jampani22fa5cb2015-04-13 15:53:44 -0700257 out.release();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700258 }
259 }
260
261 /**
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700262 * Serializes given object to byte buffer using Kryo instance in pool.
263 *
264 * @param obj Object to serialize
265 * @param buffer to write to
266 */
267 public void serialize(final Object obj, final ByteBuffer buffer) {
268 ByteBufferOutput out = new ByteBufferOutput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800269 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700270 try {
271 kryo.writeClassAndObject(out, obj);
Yuta HIGUCHIcac919c2014-10-20 22:17:20 -0700272 out.flush();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700273 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800274 release(kryo);
275 }
276 }
277
278 /**
279 * Serializes given object to OutputStream using Kryo instance in pool.
280 *
281 * @param obj Object to serialize
282 * @param stream to write to
283 */
284 public void serialize(final Object obj, final OutputStream stream) {
285 serialize(obj, stream, DEFAULT_BUFFER_SIZE);
286 }
287
288 /**
289 * Serializes given object to OutputStream using Kryo instance in pool.
290 *
291 * @param obj Object to serialize
292 * @param stream to write to
293 * @param bufferSize size of the buffer in front of the stream
294 */
295 public void serialize(final Object obj, final OutputStream stream, final int bufferSize) {
296 ByteBufferOutput out = new ByteBufferOutput(stream, bufferSize);
297 Kryo kryo = borrow();
298 try {
299 kryo.writeClassAndObject(out, obj);
300 out.flush();
301 } finally {
302 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700303 }
304 }
305
306 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700307 * Deserializes given byte array to Object using Kryo instance in pool.
308 *
309 * @param bytes serialized bytes
310 * @param <T> deserialized Object type
311 * @return deserialized Object
312 */
313 public <T> T deserialize(final byte[] bytes) {
314 Input in = new Input(bytes);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800315 Kryo kryo = borrow();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700316 try {
317 @SuppressWarnings("unchecked")
318 T obj = (T) kryo.readClassAndObject(in);
319 return obj;
320 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800321 release(kryo);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700322 }
323 }
324
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700325 /**
326 * Deserializes given byte buffer to Object using Kryo instance in pool.
327 *
328 * @param buffer input with serialized bytes
329 * @param <T> deserialized Object type
330 * @return deserialized Object
331 */
332 public <T> T deserialize(final ByteBuffer buffer) {
333 ByteBufferInput in = new ByteBufferInput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800334 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700335 try {
336 @SuppressWarnings("unchecked")
337 T obj = (T) kryo.readClassAndObject(in);
338 return obj;
339 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800340 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700341 }
342 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700343
344 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800345 * Deserializes given InputStream to an Object using Kryo instance in pool.
346 *
347 * @param stream input stream
348 * @param <T> deserialized Object type
349 * @return deserialized Object
350 */
351 public <T> T deserialize(final InputStream stream) {
352 return deserialize(stream, DEFAULT_BUFFER_SIZE);
353 }
354
355 /**
356 * Deserializes given InputStream to an Object using Kryo instance in pool.
357 *
358 * @param stream input stream
359 * @param <T> deserialized Object type
360 * @return deserialized Object
361 * @param bufferSize size of the buffer in front of the stream
362 */
363 public <T> T deserialize(final InputStream stream, final int bufferSize) {
364 ByteBufferInput in = new ByteBufferInput(stream, bufferSize);
365 Kryo kryo = borrow();
366 try {
367 @SuppressWarnings("unchecked")
368 T obj = (T) kryo.readClassAndObject(in);
369 return obj;
370 } finally {
371 release(kryo);
372 }
373 }
374
375 /**
376 * Creates a Kryo instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700377 *
378 * @return Kryo instance
379 */
Yuta HIGUCHI633cf882014-10-20 09:10:28 -0700380 @Override
381 public Kryo create() {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700382 Kryo kryo = new Kryo();
383 kryo.setRegistrationRequired(registrationRequired);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800384 for (RegistrationBlock block : registeredBlocks) {
385 int id = block.begin();
386 if (id == FLOATING_ID) {
387 id = kryo.getNextRegistrationId();
388 }
389 for (Pair<Class<?>, Serializer<?>> entry : block.types()) {
390 final Serializer<?> serializer = entry.getRight();
391 if (serializer == null) {
392 kryo.register(entry.getLeft(), id++);
393 } else {
394 kryo.register(entry.getLeft(), serializer, id++);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700395 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700396 }
397 }
398 return kryo;
399 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700400
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800401 @Override
402 public Kryo borrow() {
403 return pool.borrow();
404 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700405
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800406 @Override
407 public void release(Kryo kryo) {
408 pool.release(kryo);
409 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700410
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800411 @Override
412 public <T> T run(KryoCallback<T> callback) {
413 return pool.run(callback);
414 }
415
416 @Override
417 public String toString() {
418 return MoreObjects.toStringHelper(getClass())
419 .add("registeredBlocks", registeredBlocks)
420 .toString();
421 }
422
423 static final class RegistrationBlock {
424 private final int begin;
425 private final ImmutableList<Pair<Class<?>, Serializer<?>>> types;
426
427 public RegistrationBlock(int begin, List<Pair<Class<?>, Serializer<?>>> types) {
428 this.begin = begin;
429 this.types = ImmutableList.copyOf(types);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700430 }
431
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800432 public int begin() {
433 return begin;
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700434 }
435
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800436 public ImmutableList<Pair<Class<?>, Serializer<?>>> types() {
437 return types;
438 }
439
440 @Override
441 public String toString() {
442 return MoreObjects.toStringHelper(getClass())
443 .add("begin", begin)
444 .add("types", types)
445 .toString();
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700446 }
447 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700448}