blob: 63d53637592df62ca74eee8f4791edfb9fee07b7 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 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 HIGUCHI91768e32014-11-22 05:06:35 -080018import java.io.InputStream;
19import java.io.OutputStream;
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -070020import java.nio.ByteBuffer;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070021import java.util.ArrayList;
22import java.util.List;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070023
24import org.apache.commons.lang3.tuple.Pair;
25
26import com.esotericsoftware.kryo.Kryo;
27import com.esotericsoftware.kryo.Serializer;
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -070028import com.esotericsoftware.kryo.io.ByteBufferInput;
29import com.esotericsoftware.kryo.io.ByteBufferOutput;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070030import com.esotericsoftware.kryo.io.Input;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080031import com.esotericsoftware.kryo.pool.KryoCallback;
Yuta HIGUCHI633cf882014-10-20 09:10:28 -070032import com.esotericsoftware.kryo.pool.KryoFactory;
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080033import com.esotericsoftware.kryo.pool.KryoPool;
34import com.google.common.base.MoreObjects;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070035import com.google.common.collect.ImmutableList;
36
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070037/**
38 * Pool of Kryo instances, with classes pre-registered.
39 */
40//@ThreadSafe
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080041public final class KryoNamespace implements KryoFactory, KryoPool {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070042
43 /**
44 * Default buffer size used for serialization.
45 *
46 * @see #serialize(Object)
47 */
Yuta HIGUCHI38782052014-11-09 23:51:58 -080048 public static final int DEFAULT_BUFFER_SIZE = 4096;
Yuta HIGUCHId2a38822014-11-06 19:05:04 -080049 public static final int MAX_BUFFER_SIZE = 100 * 1000 * 1000;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070050
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080051 /**
52 * ID to use if this KryoNamespace does not define registration id.
53 */
54 public static final int FLOATING_ID = -1;
55
56 /**
57 * Smallest ID free to use for user defined registrations.
58 */
59 public static final int INITIAL_ID = 11;
60
61
62 private final KryoPool pool = new KryoPool.Builder(this)
63 .softReferences()
64 .build();
65
66 private final ImmutableList<RegistrationBlock> registeredBlocks;
67
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070068 private final boolean registrationRequired;
69
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080070
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070071 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070072 * KryoNamespace builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070073 */
74 //@NotThreadSafe
75 public static final class Builder {
76
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080077 private int blockHeadId = INITIAL_ID;
78 private List<Pair<Class<?>, Serializer<?>>> types = new ArrayList<>();
79 private List<RegistrationBlock> blocks = new ArrayList<>();
Yuta HIGUCHI2befc662014-10-30 15:57:49 -070080 private boolean registrationRequired = true;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070081
82 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070083 * Builds a {@link KryoNamespace} instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070084 *
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070085 * @return KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -070086 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -070087 public KryoNamespace build() {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -080088 if (!types.isEmpty()) {
89 blocks.add(new RegistrationBlock(this.blockHeadId, types));
90 }
91 return new KryoNamespace(blocks, registrationRequired).populate(1);
92 }
93
94 /**
95 * Sets the next Kryo registration Id for following register entries.
96 *
97 * @param id Kryo registration Id
98 * @return this
99 *
100 * @see Kryo#register(Class, Serializer, int)
101 */
102 public Builder nextId(final int id) {
103 if (!types.isEmpty()) {
104 blocks.add(new RegistrationBlock(this.blockHeadId, types));
105 types = new ArrayList<>();
106 }
107 this.blockHeadId = id;
108 return this;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700109 }
110
111 /**
112 * Registers classes to be serialized using Kryo default serializer.
113 *
114 * @param expectedTypes list of classes
115 * @return this
116 */
117 public Builder register(final Class<?>... expectedTypes) {
118 for (Class<?> clazz : expectedTypes) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800119 types.add(Pair.of(clazz, null));
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700120 }
121 return this;
122 }
123
124 /**
125 * Registers a class and it's serializer.
126 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800127 * @param classes list of classes to register
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700128 * @param serializer serializer to use for the class
129 * @return this
130 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800131 public Builder register(Serializer<?> serializer, final Class<?>... classes) {
132 for (Class<?> clazz : classes) {
133 types.add(Pair.of(clazz, serializer));
134 }
135 return this;
136 }
137
138 private Builder register(RegistrationBlock block) {
139 if (block.begin() != FLOATING_ID) {
140 // flush pending types
141 nextId(block.begin());
142 blocks.add(block);
143 nextId(block.begin() + block.types().size());
144 } else {
145 // flush pending types
146 final int addedBlockBegin = blockHeadId + types.size();
147 nextId(addedBlockBegin);
148 blocks.add(new RegistrationBlock(addedBlockBegin, block.types()));
149 nextId(addedBlockBegin + block.types().size());
150 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700151 return this;
152 }
153
154 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700155 * Registers all the class registered to given KryoNamespace.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700156 *
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800157 * @param ns KryoNamespace
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700158 * @return this
159 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800160 public Builder register(final KryoNamespace ns) {
161 for (RegistrationBlock block : ns.registeredBlocks) {
162 this.register(block);
163 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700164 return this;
165 }
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700166
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800167 /**
168 * Sets the registrationRequired flag.
169 *
170 * @param registrationRequired Kryo's registrationRequired flag
171 * @return this
172 *
173 * @see Kryo#setRegistrationRequired(boolean)
174 */
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700175 public Builder setRegistrationRequired(boolean registrationRequired) {
176 this.registrationRequired = registrationRequired;
177 return this;
178 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700179 }
180
181 /**
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700182 * Creates a new {@link KryoNamespace} builder.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700183 *
184 * @return builder
185 */
186 public static Builder newBuilder() {
187 return new Builder();
188 }
189
190 /**
191 * Creates a Kryo instance pool.
192 *
Jonathan Hart4f60f982014-10-27 08:11:17 -0700193 * @param registeredTypes types to register
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700194 * @param registrationRequired
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700195 */
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800196 private KryoNamespace(final List<RegistrationBlock> registeredTypes, boolean registrationRequired) {
197 this.registeredBlocks = ImmutableList.copyOf(registeredTypes);
Yuta HIGUCHI2befc662014-10-30 15:57:49 -0700198 this.registrationRequired = registrationRequired;
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700199 }
200
201 /**
202 * Populates the Kryo pool.
203 *
204 * @param instances to add to the pool
205 * @return this
206 */
Yuta HIGUCHI8d143d22014-10-19 23:15:09 -0700207 public KryoNamespace populate(int instances) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800208
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700209 for (int i = 0; i < instances; ++i) {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800210 release(create());
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700211 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700212 return this;
213 }
214
215 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700216 * Serializes given object to byte array using Kryo instance in pool.
217 * <p>
Yuta HIGUCHI38782052014-11-09 23:51:58 -0800218 * Note: Serialized bytes must be smaller than {@link #MAX_BUFFER_SIZE}.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700219 *
220 * @param obj Object to serialize
221 * @return serialized bytes
222 */
223 public byte[] serialize(final Object obj) {
224 return serialize(obj, DEFAULT_BUFFER_SIZE);
225 }
226
227 /**
228 * Serializes given object to byte array using Kryo instance in pool.
229 *
230 * @param obj Object to serialize
231 * @param bufferSize maximum size of serialized bytes
232 * @return serialized bytes
233 */
234 public byte[] serialize(final Object obj, final int bufferSize) {
Yuta HIGUCHId2a38822014-11-06 19:05:04 -0800235 ByteBufferOutput out = new ByteBufferOutput(bufferSize, MAX_BUFFER_SIZE);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800236 Kryo kryo = borrow();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700237 try {
238 kryo.writeClassAndObject(out, obj);
Yuta HIGUCHIcac919c2014-10-20 22:17:20 -0700239 out.flush();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700240 return out.toBytes();
241 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800242 release(kryo);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700243 }
244 }
245
246 /**
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700247 * Serializes given object to byte buffer using Kryo instance in pool.
248 *
249 * @param obj Object to serialize
250 * @param buffer to write to
251 */
252 public void serialize(final Object obj, final ByteBuffer buffer) {
253 ByteBufferOutput out = new ByteBufferOutput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800254 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700255 try {
256 kryo.writeClassAndObject(out, obj);
Yuta HIGUCHIcac919c2014-10-20 22:17:20 -0700257 out.flush();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700258 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800259 release(kryo);
260 }
261 }
262
263 /**
264 * Serializes given object to OutputStream using Kryo instance in pool.
265 *
266 * @param obj Object to serialize
267 * @param stream to write to
268 */
269 public void serialize(final Object obj, final OutputStream stream) {
270 serialize(obj, stream, DEFAULT_BUFFER_SIZE);
271 }
272
273 /**
274 * Serializes given object to OutputStream using Kryo instance in pool.
275 *
276 * @param obj Object to serialize
277 * @param stream to write to
278 * @param bufferSize size of the buffer in front of the stream
279 */
280 public void serialize(final Object obj, final OutputStream stream, final int bufferSize) {
281 ByteBufferOutput out = new ByteBufferOutput(stream, bufferSize);
282 Kryo kryo = borrow();
283 try {
284 kryo.writeClassAndObject(out, obj);
285 out.flush();
286 } finally {
287 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700288 }
289 }
290
291 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700292 * Deserializes given byte array to Object using Kryo instance in pool.
293 *
294 * @param bytes serialized bytes
295 * @param <T> deserialized Object type
296 * @return deserialized Object
297 */
298 public <T> T deserialize(final byte[] bytes) {
299 Input in = new Input(bytes);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800300 Kryo kryo = borrow();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700301 try {
302 @SuppressWarnings("unchecked")
303 T obj = (T) kryo.readClassAndObject(in);
304 return obj;
305 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800306 release(kryo);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700307 }
308 }
309
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700310 /**
311 * Deserializes given byte buffer to Object using Kryo instance in pool.
312 *
313 * @param buffer input with serialized bytes
314 * @param <T> deserialized Object type
315 * @return deserialized Object
316 */
317 public <T> T deserialize(final ByteBuffer buffer) {
318 ByteBufferInput in = new ByteBufferInput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800319 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700320 try {
321 @SuppressWarnings("unchecked")
322 T obj = (T) kryo.readClassAndObject(in);
323 return obj;
324 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800325 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700326 }
327 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700328
329 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800330 * Deserializes given InputStream to an Object using Kryo instance in pool.
331 *
332 * @param stream input stream
333 * @param <T> deserialized Object type
334 * @return deserialized Object
335 */
336 public <T> T deserialize(final InputStream stream) {
337 return deserialize(stream, DEFAULT_BUFFER_SIZE);
338 }
339
340 /**
341 * Deserializes given InputStream to an Object using Kryo instance in pool.
342 *
343 * @param stream input stream
344 * @param <T> deserialized Object type
345 * @return deserialized Object
346 * @param bufferSize size of the buffer in front of the stream
347 */
348 public <T> T deserialize(final InputStream stream, final int bufferSize) {
349 ByteBufferInput in = new ByteBufferInput(stream, bufferSize);
350 Kryo kryo = borrow();
351 try {
352 @SuppressWarnings("unchecked")
353 T obj = (T) kryo.readClassAndObject(in);
354 return obj;
355 } finally {
356 release(kryo);
357 }
358 }
359
360 /**
361 * Creates a Kryo instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700362 *
363 * @return Kryo instance
364 */
Yuta HIGUCHI633cf882014-10-20 09:10:28 -0700365 @Override
366 public Kryo create() {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700367 Kryo kryo = new Kryo();
368 kryo.setRegistrationRequired(registrationRequired);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800369 for (RegistrationBlock block : registeredBlocks) {
370 int id = block.begin();
371 if (id == FLOATING_ID) {
372 id = kryo.getNextRegistrationId();
373 }
374 for (Pair<Class<?>, Serializer<?>> entry : block.types()) {
375 final Serializer<?> serializer = entry.getRight();
376 if (serializer == null) {
377 kryo.register(entry.getLeft(), id++);
378 } else {
379 kryo.register(entry.getLeft(), serializer, id++);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700380 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700381 }
382 }
383 return kryo;
384 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700385
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800386 @Override
387 public Kryo borrow() {
388 return pool.borrow();
389 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700390
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800391 @Override
392 public void release(Kryo kryo) {
393 pool.release(kryo);
394 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700395
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800396 @Override
397 public <T> T run(KryoCallback<T> callback) {
398 return pool.run(callback);
399 }
400
401 @Override
402 public String toString() {
403 return MoreObjects.toStringHelper(getClass())
404 .add("registeredBlocks", registeredBlocks)
405 .toString();
406 }
407
408 static final class RegistrationBlock {
409 private final int begin;
410 private final ImmutableList<Pair<Class<?>, Serializer<?>>> types;
411
412 public RegistrationBlock(int begin, List<Pair<Class<?>, Serializer<?>>> types) {
413 this.begin = begin;
414 this.types = ImmutableList.copyOf(types);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700415 }
416
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800417 public int begin() {
418 return begin;
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700419 }
420
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800421 public ImmutableList<Pair<Class<?>, Serializer<?>>> types() {
422 return types;
423 }
424
425 @Override
426 public String toString() {
427 return MoreObjects.toStringHelper(getClass())
428 .add("begin", begin)
429 .add("types", types)
430 .toString();
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700431 }
432 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700433}