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 Short) {
189 writeByte(SerializationConstants.TC_SHORT);
190 writeShort((Short)serializable);
191
192 return;
193 }
194 else if (serializable instanceof Character) {
195 writeByte(SerializationConstants.TC_CHARACTER);
196 writeChar((Character)serializable);
197
198 return;
199 }
200 else if (serializable instanceof Byte) {
201 writeByte(SerializationConstants.TC_BYTE);
202 writeByte((Byte)serializable);
203
204 return;
205 }
206 else if (serializable instanceof Double) {
207 writeByte(SerializationConstants.TC_DOUBLE);
208 writeDouble((Double)serializable);
209
210 return;
211 }
212 else if (serializable instanceof Float) {
213 writeByte(SerializationConstants.TC_FLOAT);
214 writeFloat((Float)serializable);
215
216 return;
217 }
218 else {
219 writeByte(SerializationConstants.TC_CONTEXT_NAME);
220 }
221
222 ClassLoader classLoader = serializable.getClass().getClassLoader();
223
224 String contextName = ClassLoaderPool.getContextName(classLoader);
225
226 writeString(contextName);
227
228 try {
229 ObjectOutputStream objectOutputStream = new ObjectOutputStream(
230 new BufferOutputStream());
231
232 objectOutputStream.writeObject(serializable);
233
234 objectOutputStream.close();
235 }
236 catch (IOException ioe) {
237 throw new RuntimeException(
238 "Unable to write ordinary serializable object " + serializable,
239 ioe);
240 }
241 }
242
243 public void writeShort(short s) {
244 BigEndianCodec.putShort(getBuffer(2), index, s);
245
246 index += 2;
247 }
248
249 public void writeString(String s) {
250 int length = s.length();
251
252 boolean asciiCode = true;
253
254 for (int i = 0; i < length; i++) {
255 char c = s.charAt(i);
256
257 if ((c == 0) || (c > 127)) {
258 asciiCode = false;
259 break;
260 }
261 }
262
263 BigEndianCodec.putBoolean(buffer, index++, asciiCode);
264
265 if (asciiCode) {
266 byte[] buffer = getBuffer(length + 4);
267
268 BigEndianCodec.putInt(buffer, index, length);
269
270 index += 4;
271
272 for (int i = 0; i < length; i++) {
273 char c = s.charAt(i);
274
275 buffer[index++] = (byte)c;
276 }
277 }
278 else {
279 byte[] buffer = getBuffer(length * 2 + 4);
280
281 BigEndianCodec.putInt(buffer, index, length);
282
283 index += 4;
284
285 for (int i = 0; i < length; i++) {
286 char c = s.charAt(i);
287
288 BigEndianCodec.putChar(buffer, index, c);
289
290 index += 2;
291 }
292 }
293 }
294
295 public void writeTo(OutputStream outputStream) throws IOException {
296 outputStream.write(buffer, 0, index);
297
298 if (buffer.length <= THREADLOCAL_BUFFER_SIZE_LIMIT) {
299 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
300
301 bufferQueue.enqueue(buffer);
302 }
303
304 buffer = null;
305 }
306
307
315 protected final byte[] getBuffer(int ensureExtraSpace) {
316 int minSize = index + ensureExtraSpace;
317
318 if (minSize < 0) {
319
320
321
322 throw new OutOfMemoryError();
323 }
324
325 int oldSize = buffer.length;
326
327 if (minSize > oldSize) {
328 int newSize = oldSize << 1;
329
330 if (newSize < minSize) {
331
332
333
334 newSize = minSize;
335 }
336
337 buffer = Arrays.copyOf(buffer, newSize);
338 }
339
340 return buffer;
341 }
342
343
360 protected static final ThreadLocal<BufferQueue> bufferQueueThreadLocal =
361 new SoftReferenceThreadLocal<BufferQueue>() {
362
363 @Override
364 protected BufferQueue initialValue() {
365 return new BufferQueue();
366 }
367
368 };
369
370 protected static final int THREADLOCAL_BUFFER_COUNT_LIMIT;
371
372 protected static final int THREADLOCAL_BUFFER_COUNT_MIN = 8;
373
374 protected static final int THREADLOCAL_BUFFER_SIZE_LIMIT;
375
376 protected static final int THREADLOCAL_BUFFER_SIZE_MIN = 16 * 1024;
377
378 static {
379 int threadLocalBufferCountLimit = GetterUtil.getInteger(
380 System.getProperty(
381 Serializer.class.getName() +
382 ".thread.local.buffer.count.limit"));
383
384 if (threadLocalBufferCountLimit < THREADLOCAL_BUFFER_COUNT_MIN) {
385 threadLocalBufferCountLimit = THREADLOCAL_BUFFER_COUNT_MIN;
386 }
387
388 THREADLOCAL_BUFFER_COUNT_LIMIT = threadLocalBufferCountLimit;
389
390 int threadLocalBufferSizeLimit = GetterUtil.getInteger(
391 System.getProperty(
392 Serializer.class.getName() +
393 ".thread.local.buffer.size.limit"));
394
395 if (threadLocalBufferSizeLimit < THREADLOCAL_BUFFER_SIZE_MIN) {
396 threadLocalBufferSizeLimit = THREADLOCAL_BUFFER_SIZE_MIN;
397 }
398
399 THREADLOCAL_BUFFER_SIZE_LIMIT = threadLocalBufferSizeLimit;
400 }
401
402 protected byte[] buffer;
403 protected int index;
404
405 protected static class BufferNode {
406
407 public BufferNode(byte[] buffer) {
408 this.buffer = buffer;
409 }
410
411 protected byte[] buffer;
412 protected BufferNode next;
413
414 }
415
416
426 protected static class BufferQueue {
427
428 public void enqueue(byte[] buffer) {
429 BufferNode bufferNode = new BufferNode(buffer);
430
431 if (headBufferNode == null) {
432 headBufferNode = bufferNode;
433
434 count = 1;
435
436 return;
437 }
438
439 BufferNode previousBufferNode = null;
440 BufferNode currentBufferNode = headBufferNode;
441
442 while ((currentBufferNode != null) &&
443 (currentBufferNode.buffer.length >
444 bufferNode.buffer.length)) {
445
446 previousBufferNode = currentBufferNode;
447
448 currentBufferNode = currentBufferNode.next;
449 }
450
451 if (previousBufferNode == null) {
452 bufferNode.next = headBufferNode;
453
454 headBufferNode = bufferNode;
455 }
456 else {
457 bufferNode.next = currentBufferNode;
458
459 previousBufferNode.next = bufferNode;
460 }
461
462 if (++count > THREADLOCAL_BUFFER_COUNT_LIMIT) {
463 if (previousBufferNode == null) {
464 previousBufferNode = headBufferNode;
465 }
466
467 currentBufferNode = previousBufferNode.next;
468
469 while (currentBufferNode.next != null) {
470 previousBufferNode = currentBufferNode;
471 currentBufferNode = currentBufferNode.next;
472 }
473
474
475
476 previousBufferNode.next = null;
477
478
479
480 currentBufferNode.buffer = null;
481 currentBufferNode.next = null;
482 }
483 }
484
485 public byte[] dequeue() {
486 if (headBufferNode == null) {
487 return new byte[THREADLOCAL_BUFFER_SIZE_MIN];
488 }
489
490 BufferNode bufferNode = headBufferNode;
491
492 headBufferNode = headBufferNode.next;
493
494
495
496 bufferNode.next = null;
497
498 return bufferNode.buffer;
499 }
500
501 protected int count;
502 protected BufferNode headBufferNode;
503
504 }
505
506 protected class BufferOutputStream extends OutputStream {
507
508 @Override
509 public void write(byte[] bytes) {
510 write(bytes, 0, bytes.length);
511 }
512
513 @Override
514 public void write(byte[] bytes, int offset, int length) {
515 byte[] buffer = getBuffer(length);
516
517 System.arraycopy(bytes, offset, buffer, index, length);
518
519 index += length;
520 }
521
522 @Override
523 public void write(int b) {
524 getBuffer(1)[index++] = (byte)b;
525 }
526
527 }
528
529 }