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 BigEndianCodec.putBoolean(buffer, index++, asciiCode);
271
272 if (asciiCode) {
273 byte[] buffer = getBuffer(length + 4);
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 + 4);
287
288 BigEndianCodec.putInt(buffer, index, length);
289
290 index += 4;
291
292 for (int i = 0; i < length; i++) {
293 char c = s.charAt(i);
294
295 BigEndianCodec.putChar(buffer, index, c);
296
297 index += 2;
298 }
299 }
300 }
301
302 public void writeTo(OutputStream outputStream) throws IOException {
303 outputStream.write(buffer, 0, index);
304
305 if (buffer.length <= THREADLOCAL_BUFFER_SIZE_LIMIT) {
306 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
307
308 bufferQueue.enqueue(buffer);
309 }
310
311 buffer = null;
312 }
313
314
322 protected final byte[] getBuffer(int ensureExtraSpace) {
323 int minSize = index + ensureExtraSpace;
324
325 if (minSize < 0) {
326
327
328
329 throw new OutOfMemoryError();
330 }
331
332 int oldSize = buffer.length;
333
334 if (minSize > oldSize) {
335 int newSize = oldSize << 1;
336
337 if (newSize < minSize) {
338
339
340
341 newSize = minSize;
342 }
343
344 buffer = Arrays.copyOf(buffer, newSize);
345 }
346
347 return buffer;
348 }
349
350
367 protected static final ThreadLocal<BufferQueue> bufferQueueThreadLocal =
368 new SoftReferenceThreadLocal<BufferQueue>() {
369
370 @Override
371 protected BufferQueue initialValue() {
372 return new BufferQueue();
373 }
374
375 };
376
377 protected static final int THREADLOCAL_BUFFER_COUNT_LIMIT;
378
379 protected static final int THREADLOCAL_BUFFER_COUNT_MIN = 8;
380
381 protected static final int THREADLOCAL_BUFFER_SIZE_LIMIT;
382
383 protected static final int THREADLOCAL_BUFFER_SIZE_MIN = 16 * 1024;
384
385 static {
386 int threadLocalBufferCountLimit = GetterUtil.getInteger(
387 System.getProperty(
388 Serializer.class.getName() +
389 ".thread.local.buffer.count.limit"));
390
391 if (threadLocalBufferCountLimit < THREADLOCAL_BUFFER_COUNT_MIN) {
392 threadLocalBufferCountLimit = THREADLOCAL_BUFFER_COUNT_MIN;
393 }
394
395 THREADLOCAL_BUFFER_COUNT_LIMIT = threadLocalBufferCountLimit;
396
397 int threadLocalBufferSizeLimit = GetterUtil.getInteger(
398 System.getProperty(
399 Serializer.class.getName() +
400 ".thread.local.buffer.size.limit"));
401
402 if (threadLocalBufferSizeLimit < THREADLOCAL_BUFFER_SIZE_MIN) {
403 threadLocalBufferSizeLimit = THREADLOCAL_BUFFER_SIZE_MIN;
404 }
405
406 THREADLOCAL_BUFFER_SIZE_LIMIT = threadLocalBufferSizeLimit;
407 }
408
409 protected byte[] buffer;
410 protected int index;
411
412 protected static class BufferNode {
413
414 public BufferNode(byte[] buffer) {
415 this.buffer = buffer;
416 }
417
418 protected byte[] buffer;
419 protected BufferNode next;
420
421 }
422
423
433 protected static class BufferQueue {
434
435 public void enqueue(byte[] buffer) {
436 BufferNode bufferNode = new BufferNode(buffer);
437
438 if (headBufferNode == null) {
439 headBufferNode = bufferNode;
440
441 count = 1;
442
443 return;
444 }
445
446 BufferNode previousBufferNode = null;
447 BufferNode currentBufferNode = headBufferNode;
448
449 while ((currentBufferNode != null) &&
450 (currentBufferNode.buffer.length >
451 bufferNode.buffer.length)) {
452
453 previousBufferNode = currentBufferNode;
454
455 currentBufferNode = currentBufferNode.next;
456 }
457
458 if (previousBufferNode == null) {
459 bufferNode.next = headBufferNode;
460
461 headBufferNode = bufferNode;
462 }
463 else {
464 bufferNode.next = currentBufferNode;
465
466 previousBufferNode.next = bufferNode;
467 }
468
469 if (++count > THREADLOCAL_BUFFER_COUNT_LIMIT) {
470 if (previousBufferNode == null) {
471 previousBufferNode = headBufferNode;
472 }
473
474 currentBufferNode = previousBufferNode.next;
475
476 while (currentBufferNode.next != null) {
477 previousBufferNode = currentBufferNode;
478 currentBufferNode = currentBufferNode.next;
479 }
480
481
482
483 previousBufferNode.next = null;
484
485
486
487 currentBufferNode.buffer = null;
488 currentBufferNode.next = null;
489 }
490 }
491
492 public byte[] dequeue() {
493 if (headBufferNode == null) {
494 return new byte[THREADLOCAL_BUFFER_SIZE_MIN];
495 }
496
497 BufferNode bufferNode = headBufferNode;
498
499 headBufferNode = headBufferNode.next;
500
501
502
503 bufferNode.next = null;
504
505 return bufferNode.buffer;
506 }
507
508 protected int count;
509 protected BufferNode headBufferNode;
510
511 }
512
513 protected class BufferOutputStream extends OutputStream {
514
515 @Override
516 public void write(byte[] bytes) {
517 write(bytes, 0, bytes.length);
518 }
519
520 @Override
521 public void write(byte[] bytes, int offset, int length) {
522 byte[] buffer = getBuffer(length);
523
524 System.arraycopy(bytes, offset, buffer, index, length);
525
526 index += length;
527 }
528
529 @Override
530 public void write(int b) {
531 getBuffer(1)[index++] = (byte)b;
532 }
533
534 }
535
536 }