blob: 9977e35df1f80e18053fa2fd5dd479478cf13ad3 [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
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 HIGUCHI24a086b2014-09-21 23:28:41 -0700236 try {
Madan Jampani22fa5cb2015-04-13 15:53:44 -0700237 Kryo kryo = borrow();
238 try {
239 kryo.writeClassAndObject(out, obj);
240 out.flush();
241 return out.toBytes();
242 } finally {
243 release(kryo);
244 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700245 } finally {
Madan Jampani22fa5cb2015-04-13 15:53:44 -0700246 out.release();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700247 }
248 }
249
250 /**
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700251 * Serializes given object to byte buffer using Kryo instance in pool.
252 *
253 * @param obj Object to serialize
254 * @param buffer to write to
255 */
256 public void serialize(final Object obj, final ByteBuffer buffer) {
257 ByteBufferOutput out = new ByteBufferOutput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800258 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700259 try {
260 kryo.writeClassAndObject(out, obj);
Yuta HIGUCHIcac919c2014-10-20 22:17:20 -0700261 out.flush();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700262 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800263 release(kryo);
264 }
265 }
266
267 /**
268 * Serializes given object to OutputStream using Kryo instance in pool.
269 *
270 * @param obj Object to serialize
271 * @param stream to write to
272 */
273 public void serialize(final Object obj, final OutputStream stream) {
274 serialize(obj, stream, DEFAULT_BUFFER_SIZE);
275 }
276
277 /**
278 * Serializes given object to OutputStream using Kryo instance in pool.
279 *
280 * @param obj Object to serialize
281 * @param stream to write to
282 * @param bufferSize size of the buffer in front of the stream
283 */
284 public void serialize(final Object obj, final OutputStream stream, final int bufferSize) {
285 ByteBufferOutput out = new ByteBufferOutput(stream, bufferSize);
286 Kryo kryo = borrow();
287 try {
288 kryo.writeClassAndObject(out, obj);
289 out.flush();
290 } finally {
291 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700292 }
293 }
294
295 /**
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700296 * Deserializes given byte array to Object using Kryo instance in pool.
297 *
298 * @param bytes serialized bytes
299 * @param <T> deserialized Object type
300 * @return deserialized Object
301 */
302 public <T> T deserialize(final byte[] bytes) {
303 Input in = new Input(bytes);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800304 Kryo kryo = borrow();
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700305 try {
306 @SuppressWarnings("unchecked")
307 T obj = (T) kryo.readClassAndObject(in);
308 return obj;
309 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800310 release(kryo);
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700311 }
312 }
313
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700314 /**
315 * Deserializes given byte buffer to Object using Kryo instance in pool.
316 *
317 * @param buffer input with serialized bytes
318 * @param <T> deserialized Object type
319 * @return deserialized Object
320 */
321 public <T> T deserialize(final ByteBuffer buffer) {
322 ByteBufferInput in = new ByteBufferInput(buffer);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800323 Kryo kryo = borrow();
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700324 try {
325 @SuppressWarnings("unchecked")
326 T obj = (T) kryo.readClassAndObject(in);
327 return obj;
328 } finally {
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800329 release(kryo);
Yuta HIGUCHIf4b107e2014-09-29 17:27:26 -0700330 }
331 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700332
333 /**
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800334 * Deserializes given InputStream to an Object using Kryo instance in pool.
335 *
336 * @param stream input stream
337 * @param <T> deserialized Object type
338 * @return deserialized Object
339 */
340 public <T> T deserialize(final InputStream stream) {
341 return deserialize(stream, DEFAULT_BUFFER_SIZE);
342 }
343
344 /**
345 * 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 * @param bufferSize size of the buffer in front of the stream
351 */
352 public <T> T deserialize(final InputStream stream, final int bufferSize) {
353 ByteBufferInput in = new ByteBufferInput(stream, bufferSize);
354 Kryo kryo = borrow();
355 try {
356 @SuppressWarnings("unchecked")
357 T obj = (T) kryo.readClassAndObject(in);
358 return obj;
359 } finally {
360 release(kryo);
361 }
362 }
363
364 /**
365 * Creates a Kryo instance.
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700366 *
367 * @return Kryo instance
368 */
Yuta HIGUCHI633cf882014-10-20 09:10:28 -0700369 @Override
370 public Kryo create() {
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700371 Kryo kryo = new Kryo();
372 kryo.setRegistrationRequired(registrationRequired);
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800373 for (RegistrationBlock block : registeredBlocks) {
374 int id = block.begin();
375 if (id == FLOATING_ID) {
376 id = kryo.getNextRegistrationId();
377 }
378 for (Pair<Class<?>, Serializer<?>> entry : block.types()) {
379 final Serializer<?> serializer = entry.getRight();
380 if (serializer == null) {
381 kryo.register(entry.getLeft(), id++);
382 } else {
383 kryo.register(entry.getLeft(), serializer, id++);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700384 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700385 }
386 }
387 return kryo;
388 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700389
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800390 @Override
391 public Kryo borrow() {
392 return pool.borrow();
393 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700394
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800395 @Override
396 public void release(Kryo kryo) {
397 pool.release(kryo);
398 }
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700399
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800400 @Override
401 public <T> T run(KryoCallback<T> callback) {
402 return pool.run(callback);
403 }
404
405 @Override
406 public String toString() {
407 return MoreObjects.toStringHelper(getClass())
408 .add("registeredBlocks", registeredBlocks)
409 .toString();
410 }
411
412 static final class RegistrationBlock {
413 private final int begin;
414 private final ImmutableList<Pair<Class<?>, Serializer<?>>> types;
415
416 public RegistrationBlock(int begin, List<Pair<Class<?>, Serializer<?>>> types) {
417 this.begin = begin;
418 this.types = ImmutableList.copyOf(types);
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700419 }
420
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800421 public int begin() {
422 return begin;
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700423 }
424
Yuta HIGUCHI91768e32014-11-22 05:06:35 -0800425 public ImmutableList<Pair<Class<?>, Serializer<?>>> types() {
426 return types;
427 }
428
429 @Override
430 public String toString() {
431 return MoreObjects.toStringHelper(getClass())
432 .add("begin", begin)
433 .add("types", types)
434 .toString();
Yuta HIGUCHI533ec322014-09-30 13:29:52 -0700435 }
436 }
Yuta HIGUCHI24a086b2014-09-21 23:28:41 -0700437}