001
014
015 package com.liferay.portal.kernel.io;
016
017 import com.liferay.portal.kernel.memory.SoftReferenceThreadLocal;
018 import com.liferay.portal.kernel.util.ClassLoaderPool;
019 import com.liferay.portal.kernel.util.GetterUtil;
020
021 import java.io.IOException;
022 import java.io.ObjectOutputStream;
023 import java.io.OutputStream;
024 import java.io.Serializable;
025
026 import java.nio.ByteBuffer;
027
028 import java.util.Arrays;
029
030
095 public class Serializer {
096
097 public Serializer() {
098 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
099
100 buffer = bufferQueue.dequeue();
101 }
102
103 public ByteBuffer toByteBuffer() {
104 ByteBuffer byteBuffer = ByteBuffer.wrap(Arrays.copyOf(buffer, index));
105
106 if (buffer.length <= THREADLOCAL_BUFFER_SIZE_LIMIT) {
107 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
108
109 bufferQueue.enqueue(buffer);
110 }
111
112 buffer = null;
113
114 return byteBuffer;
115 }
116
117 public void writeBoolean(boolean b) {
118 BigEndianCodec.putBoolean(getBuffer(1), index++, b);
119 }
120
121 public void writeByte(byte b) {
122 getBuffer(1)[index++] = b;
123 }
124
125 public void writeChar(char c) {
126 BigEndianCodec.putChar(getBuffer(2), index, c);
127
128 index += 2;
129 }
130
131 public void writeDouble(double d) {
132 BigEndianCodec.putDouble(getBuffer(8), index, d);
133
134 index += 8;
135 }
136
137 public void writeFloat(float f) {
138 BigEndianCodec.putFloat(getBuffer(4), index, f);
139
140 index += 4;
141 }
142
143 public void writeInt(int i) {
144 BigEndianCodec.putInt(getBuffer(4), index, i);
145
146 index += 4;
147 }
148
149 public void writeLong(long l) {
150 BigEndianCodec.putLong(getBuffer(8), index, l);
151
152 index += 8;
153 }
154
155 public void writeObject(Serializable serializable) {
156
157
158
159 if (serializable == null) {
160 writeByte(SerializationConstants.TC_NULL);
161
162 return;
163 }
164 else if (serializable instanceof Long) {
165 writeByte(SerializationConstants.TC_LONG);
166 writeLong((Long)serializable);
167
168 return;
169 }
170 else if (serializable instanceof String) {
171 writeByte(SerializationConstants.TC_STRING);
172 writeString((String)serializable);
173
174 return;
175 }
176 else if (serializable instanceof Integer) {
177 writeByte(SerializationConstants.TC_INTEGER);
178 writeInt((Integer)serializable);
179
180 return;
181 }
182 else if (serializable instanceof Boolean) {
183 writeByte(SerializationConstants.TC_BOOLEAN);
184 writeBoolean((Boolean)serializable);
185
186 return;
187 }
188 else if (serializable instanceof Class) {
189 Class<?> clazz = (Class<?>)serializable;
190
191 ClassLoader classLoader = clazz.getClassLoader();
192
193 String contextName = ClassLoaderPool.getContextName(classLoader);
194
195 writeByte(SerializationConstants.TC_CLASS);
196 writeString(contextName);
197 writeString(clazz.getName());
198
199 return;
200 }
201 else if (serializable instanceof Short) {
202 writeByte(SerializationConstants.TC_SHORT);
203 writeShort((Short)serializable);
204
205 return;
206 }
207 else if (serializable instanceof Character) {
208 writeByte(SerializationConstants.TC_CHARACTER);
209 writeChar((Character)serializable);
210
211 return;
212 }
213 else if (serializable instanceof Byte) {
214 writeByte(SerializationConstants.TC_BYTE);
215 writeByte((Byte)serializable);
216
217 return;
218 }
219 else if (serializable instanceof Double) {
220 writeByte(SerializationConstants.TC_DOUBLE);
221 writeDouble((Double)serializable);
222
223 return;
224 }
225 else if (serializable instanceof Float) {
226 writeByte(SerializationConstants.TC_FLOAT);
227 writeFloat((Float)serializable);
228
229 return;
230 }
231 else {
232 writeByte(SerializationConstants.TC_OBJECT);
233 }
234
235 try {
236 ObjectOutputStream objectOutputStream =
237 new AnnotatedObjectOutputStream(new BufferOutputStream());
238
239 objectOutputStream.writeObject(serializable);
240
241 objectOutputStream.close();
242 }
243 catch (IOException ioe) {
244 throw new RuntimeException(
245 "Unable to write ordinary serializable object " + serializable,
246 ioe);
247 }
248 }
249
250 public void writeShort(short s) {
251 BigEndianCodec.putShort(getBuffer(2), index, s);
252
253 index += 2;
254 }
255
256 public void writeString(String s) {
257 int length = s.length();
258
259 boolean asciiCode = true;
260
261 for (int i = 0; i < length; i++) {
262 char c = s.charAt(i);
263
264 if ((c == 0) || (c > 127)) {
265 asciiCode = false;
266 break;
267 }
268 }
269
270 if (asciiCode) {
271 byte[] buffer = getBuffer(length + 5);
272
273 BigEndianCodec.putBoolean(buffer, index++, asciiCode);
274
275 BigEndianCodec.putInt(buffer, index, length);
276
277 index += 4;
278
279 for (int i = 0; i < length; i++) {
280 char c = s.charAt(i);
281
282 buffer[index++] = (byte)c;
283 }
284 }
285 else {
286 byte[] buffer = getBuffer(length * 2 + 5);
287
288 BigEndianCodec.putBoolean(buffer, index++, asciiCode);
289
290 BigEndianCodec.putInt(buffer, index, length);
291
292 index += 4;
293
294 for (int i = 0; i < length; i++) {
295 char c = s.charAt(i);
296
297 BigEndianCodec.putChar(buffer, index, c);
298
299 index += 2;
300 }
301 }
302 }
303
304 public void writeTo(OutputStream outputStream) throws IOException {
305 outputStream.write(buffer, 0, index);
306
307 if (buffer.length <= THREADLOCAL_BUFFER_SIZE_LIMIT) {
308 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
309
310 bufferQueue.enqueue(buffer);
311 }
312
313 buffer = null;
314 }
315
316
324 protected final byte[] getBuffer(int ensureExtraSpace) {
325 int minSize = index + ensureExtraSpace;
326
327 if (minSize < 0) {
328
329
330
331 throw new OutOfMemoryError();
332 }
333
334 int oldSize = buffer.length;
335
336 if (minSize > oldSize) {
337 int newSize = oldSize << 1;
338
339 if (newSize < minSize) {
340
341
342
343 newSize = minSize;
344 }
345
346 buffer = Arrays.copyOf(buffer, newSize);
347 }
348
349 return buffer;
350 }
351
352 protected static final int THREADLOCAL_BUFFER_COUNT_LIMIT;
353
354 protected static final int THREADLOCAL_BUFFER_COUNT_MIN = 8;
355
356 protected static final int THREADLOCAL_BUFFER_SIZE_LIMIT;
357
358 protected static final int THREADLOCAL_BUFFER_SIZE_MIN = 16 * 1024;
359
360 static {
361 int threadLocalBufferCountLimit = GetterUtil.getInteger(
362 System.getProperty(
363 Serializer.class.getName() +
364 ".thread.local.buffer.count.limit"));
365
366 if (threadLocalBufferCountLimit < THREADLOCAL_BUFFER_COUNT_MIN) {
367 threadLocalBufferCountLimit = THREADLOCAL_BUFFER_COUNT_MIN;
368 }
369
370 THREADLOCAL_BUFFER_COUNT_LIMIT = threadLocalBufferCountLimit;
371
372 int threadLocalBufferSizeLimit = GetterUtil.getInteger(
373 System.getProperty(
374 Serializer.class.getName() +
375 ".thread.local.buffer.size.limit"));
376
377 if (threadLocalBufferSizeLimit < THREADLOCAL_BUFFER_SIZE_MIN) {
378 threadLocalBufferSizeLimit = THREADLOCAL_BUFFER_SIZE_MIN;
379 }
380
381 THREADLOCAL_BUFFER_SIZE_LIMIT = threadLocalBufferSizeLimit;
382 }
383
384
401 protected static final ThreadLocal<BufferQueue> bufferQueueThreadLocal =
402 new SoftReferenceThreadLocal<BufferQueue>() {
403
404 @Override
405 protected BufferQueue initialValue() {
406 return new BufferQueue();
407 }
408
409 };
410
411 protected byte[] buffer;
412 protected int index;
413
414 protected static class BufferNode {
415
416 public BufferNode(byte[] buffer) {
417 this.buffer = buffer;
418 }
419
420 protected byte[] buffer;
421 protected BufferNode next;
422
423 }
424
425
435 protected static class BufferQueue {
436
437 public void enqueue(byte[] buffer) {
438 BufferNode bufferNode = new BufferNode(buffer);
439
440 if (headBufferNode == null) {
441 headBufferNode = bufferNode;
442
443 count = 1;
444
445 return;
446 }
447
448 BufferNode previousBufferNode = null;
449 BufferNode currentBufferNode = headBufferNode;
450
451 while ((currentBufferNode != null) &&
452 (currentBufferNode.buffer.length >
453 bufferNode.buffer.length)) {
454
455 previousBufferNode = currentBufferNode;
456
457 currentBufferNode = currentBufferNode.next;
458 }
459
460 if (previousBufferNode == null) {
461 bufferNode.next = headBufferNode;
462
463 headBufferNode = bufferNode;
464 }
465 else {
466 bufferNode.next = currentBufferNode;
467
468 previousBufferNode.next = bufferNode;
469 }
470
471 if (++count > THREADLOCAL_BUFFER_COUNT_LIMIT) {
472 if (previousBufferNode == null) {
473 previousBufferNode = headBufferNode;
474 }
475
476 currentBufferNode = previousBufferNode.next;
477
478 while (currentBufferNode.next != null) {
479 previousBufferNode = currentBufferNode;
480 currentBufferNode = currentBufferNode.next;
481 }
482
483
484
485 previousBufferNode.next = null;
486
487
488
489 currentBufferNode.buffer = null;
490 currentBufferNode.next = null;
491 }
492 }
493
494 public byte[] dequeue() {
495 if (headBufferNode == null) {
496 return new byte[THREADLOCAL_BUFFER_SIZE_MIN];
497 }
498
499 BufferNode bufferNode = headBufferNode;
500
501 headBufferNode = headBufferNode.next;
502
503
504
505 bufferNode.next = null;
506
507 return bufferNode.buffer;
508 }
509
510 protected int count;
511 protected BufferNode headBufferNode;
512
513 }
514
515 protected class BufferOutputStream extends OutputStream {
516
517 @Override
518 public void write(byte[] bytes) {
519 write(bytes, 0, bytes.length);
520 }
521
522 @Override
523 public void write(byte[] bytes, int offset, int length) {
524 byte[] buffer = getBuffer(length);
525
526 System.arraycopy(bytes, offset, buffer, index, length);
527
528 index += length;
529 }
530
531 @Override
532 public void write(int b) {
533 getBuffer(1)[index++] = (byte)b;
534 }
535
536 }
537
538 }