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
032 public class Serializer {
033
034 public Serializer() {
035 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
036
037 buffer = bufferQueue.dequeue();
038 }
039
040 public ByteBuffer toByteBuffer() {
041 byte[] tempBuffer = new byte[index];
042
043 System.arraycopy(buffer, 0, tempBuffer, 0, index);
044
045 ByteBuffer byteBuffer = ByteBuffer.wrap(tempBuffer);
046
047 if (buffer.length <= THREADLOCAL_BUFFER_SIZE_LIMIT) {
048 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
049
050 bufferQueue.enqueue(buffer);
051 }
052
053 buffer = null;
054
055 return byteBuffer;
056 }
057
058 public void writeBoolean(boolean b) {
059 BigEndianCodec.putBoolean(getBuffer(1), index++, b);
060 }
061
062 public void writeByte(byte b) {
063 getBuffer(1)[index++] = b;
064 }
065
066 public void writeChar(char c) {
067 BigEndianCodec.putChar(getBuffer(2), index, c);
068
069 index += 2;
070 }
071
072 public void writeDouble(double d) {
073 BigEndianCodec.putDouble(getBuffer(8), index, d);
074
075 index += 8;
076 }
077
078 public void writeFloat(float f) {
079 BigEndianCodec.putFloat(getBuffer(4), index, f);
080
081 index += 4;
082 }
083
084 public void writeInt(int i) {
085 BigEndianCodec.putInt(getBuffer(4), index, i);
086
087 index += 4;
088 }
089
090 public void writeLong(long l) {
091 BigEndianCodec.putLong(getBuffer(8), index, l);
092
093 index += 8;
094 }
095
096 public void writeObject(Serializable serializable) {
097
098
099
100 if (serializable == null) {
101 writeByte(SerializationConstants.TC_NULL);
102
103 return;
104 }
105 else if (serializable instanceof Long) {
106 writeByte(SerializationConstants.TC_LONG);
107 writeLong((Long)serializable);
108
109 return;
110 }
111 else if (serializable instanceof String) {
112 writeByte(SerializationConstants.TC_STRING);
113 writeString((String)serializable);
114
115 return;
116 }
117 else if (serializable instanceof Integer) {
118 writeByte(SerializationConstants.TC_INTEGER);
119 writeInt((Integer)serializable);
120
121 return;
122 }
123 else if (serializable instanceof Boolean) {
124 writeByte(SerializationConstants.TC_BOOLEAN);
125 writeBoolean((Boolean)serializable);
126
127 return;
128 }
129 else if (serializable instanceof Class) {
130 Class<?> clazz = (Class<?>)serializable;
131
132 ClassLoader classLoader = clazz.getClassLoader();
133
134 String contextName = ClassLoaderPool.getContextName(classLoader);
135
136 writeByte(SerializationConstants.TC_CLASS);
137 writeString(contextName);
138 writeString(clazz.getName());
139
140 return;
141 }
142 else if (serializable instanceof Short) {
143 writeByte(SerializationConstants.TC_SHORT);
144 writeShort((Short)serializable);
145
146 return;
147 }
148 else if (serializable instanceof Character) {
149 writeByte(SerializationConstants.TC_CHARACTER);
150 writeChar((Character)serializable);
151
152 return;
153 }
154 else if (serializable instanceof Byte) {
155 writeByte(SerializationConstants.TC_BYTE);
156 writeByte((Byte)serializable);
157
158 return;
159 }
160 else if (serializable instanceof Double) {
161 writeByte(SerializationConstants.TC_DOUBLE);
162 writeDouble((Double)serializable);
163
164 return;
165 }
166 else if (serializable instanceof Float) {
167 writeByte(SerializationConstants.TC_FLOAT);
168 writeFloat((Float)serializable);
169
170 return;
171 }
172 else {
173 writeByte(SerializationConstants.TC_OBJECT);
174 }
175
176 try {
177 ObjectOutputStream objectOutputStream =
178 new AnnotatedObjectOutputStream(new BufferOutputStream());
179
180 objectOutputStream.writeObject(serializable);
181
182 objectOutputStream.close();
183 }
184 catch (IOException ioe) {
185 throw new RuntimeException(
186 "Unable to write ordinary serializable object " + serializable,
187 ioe);
188 }
189 }
190
191 public void writeShort(short s) {
192 BigEndianCodec.putShort(getBuffer(2), index, s);
193
194 index += 2;
195 }
196
197 public void writeString(String s) {
198 int length = s.length();
199
200 boolean asciiCode = true;
201
202 for (int i = 0; i < length; i++) {
203 char c = s.charAt(i);
204
205 if ((c == 0) || (c > 127)) {
206 asciiCode = false;
207 break;
208 }
209 }
210
211 BigEndianCodec.putBoolean(buffer, index++, asciiCode);
212
213 if (asciiCode) {
214 byte[] buffer = getBuffer(length + 4);
215
216 BigEndianCodec.putInt(buffer, index, length);
217
218 index += 4;
219
220 for (int i = 0; i < length; i++) {
221 char c = s.charAt(i);
222
223 buffer[index++] = (byte)c;
224 }
225 }
226 else {
227 byte[] buffer = getBuffer(length * 2 + 4);
228
229 BigEndianCodec.putInt(buffer, index, length);
230
231 index += 4;
232
233 for (int i = 0; i < length; i++) {
234 char c = s.charAt(i);
235
236 BigEndianCodec.putChar(buffer, index, c);
237
238 index += 2;
239 }
240 }
241 }
242
243 public void writeTo(OutputStream outputStream) throws IOException {
244 outputStream.write(buffer, 0, index);
245
246 if (buffer.length <= THREADLOCAL_BUFFER_SIZE_LIMIT) {
247 BufferQueue bufferQueue = bufferQueueThreadLocal.get();
248
249 bufferQueue.enqueue(buffer);
250 }
251
252 buffer = null;
253 }
254
255
258 protected final byte[] getBuffer(int ensureExtraSpace) {
259 int minSize = index + ensureExtraSpace;
260
261 if (minSize < 0) {
262
263
264
265 throw new OutOfMemoryError();
266 }
267
268 int oldSize = buffer.length;
269
270 if (minSize > oldSize) {
271 int newSize = oldSize << 1;
272
273 if (newSize < minSize) {
274
275
276
277 newSize = minSize;
278 }
279
280 byte[] tempBuffer = new byte[newSize];
281
282 System.arraycopy(buffer, 0, tempBuffer, 0, oldSize);
283
284 buffer = tempBuffer;
285 }
286
287 return buffer;
288 }
289
290 protected static final ThreadLocal<BufferQueue> bufferQueueThreadLocal =
291 new SoftReferenceThreadLocal<BufferQueue>() {
292
293 @Override
294 protected BufferQueue initialValue() {
295 return new BufferQueue();
296 }
297
298 };
299
300 protected static final int THREADLOCAL_BUFFER_COUNT_LIMIT;
301
302 protected static final int THREADLOCAL_BUFFER_COUNT_MIN = 8;
303
304 protected static final int THREADLOCAL_BUFFER_SIZE_LIMIT;
305
306 protected static final int THREADLOCAL_BUFFER_SIZE_MIN = 16 * 1024;
307
308 static {
309 int threadLocalBufferCountLimit = GetterUtil.getInteger(
310 System.getProperty(
311 Serializer.class.getName() +
312 ".thread.local.buffer.count.limit"));
313
314 if (threadLocalBufferCountLimit < THREADLOCAL_BUFFER_COUNT_MIN) {
315 threadLocalBufferCountLimit = THREADLOCAL_BUFFER_COUNT_MIN;
316 }
317
318 THREADLOCAL_BUFFER_COUNT_LIMIT = threadLocalBufferCountLimit;
319
320 int threadLocalBufferSizeLimit = GetterUtil.getInteger(
321 System.getProperty(
322 Serializer.class.getName() +
323 ".thread.local.buffer.size.limit"));
324
325 if (threadLocalBufferSizeLimit < THREADLOCAL_BUFFER_SIZE_MIN) {
326 threadLocalBufferSizeLimit = THREADLOCAL_BUFFER_SIZE_MIN;
327 }
328
329 THREADLOCAL_BUFFER_SIZE_LIMIT = threadLocalBufferSizeLimit;
330 }
331
332 protected byte[] buffer;
333 protected int index;
334
335 protected static class BufferNode {
336
337 public BufferNode(byte[] buffer) {
338 this.buffer = buffer;
339 }
340
341 protected byte[] buffer;
342 protected BufferNode next;
343
344 }
345
346 protected static class BufferQueue {
347
348 public void enqueue(byte[] buffer) {
349 BufferNode bufferNode = new BufferNode(buffer);
350
351 if (headBufferNode == null) {
352 headBufferNode = bufferNode;
353
354 count = 1;
355
356 return;
357 }
358
359 BufferNode previousBufferNode = null;
360 BufferNode currentBufferNode = headBufferNode;
361
362 while ((currentBufferNode != null) &&
363 (currentBufferNode.buffer.length >
364 bufferNode.buffer.length)) {
365
366 previousBufferNode = currentBufferNode;
367
368 currentBufferNode = currentBufferNode.next;
369 }
370
371 if (previousBufferNode == null) {
372 bufferNode.next = headBufferNode;
373
374 headBufferNode = bufferNode;
375 }
376 else {
377 bufferNode.next = currentBufferNode;
378
379 previousBufferNode.next = bufferNode;
380 }
381
382 if (++count > THREADLOCAL_BUFFER_COUNT_LIMIT) {
383 if (previousBufferNode == null) {
384 previousBufferNode = headBufferNode;
385 }
386
387 currentBufferNode = previousBufferNode.next;
388
389 while (currentBufferNode.next != null) {
390 previousBufferNode = currentBufferNode;
391 currentBufferNode = currentBufferNode.next;
392 }
393
394
395
396 previousBufferNode.next = null;
397
398
399
400 currentBufferNode.buffer = null;
401 currentBufferNode.next = null;
402 }
403 }
404
405 public byte[] dequeue() {
406 if (headBufferNode == null) {
407 return new byte[THREADLOCAL_BUFFER_SIZE_MIN];
408 }
409
410 BufferNode bufferNode = headBufferNode;
411
412 headBufferNode = headBufferNode.next;
413
414
415
416 bufferNode.next = null;
417
418 return bufferNode.buffer;
419 }
420
421 protected int count;
422 protected BufferNode headBufferNode;
423
424 }
425
426 protected class BufferOutputStream extends OutputStream {
427
428 @Override
429 public void write(byte[] bytes) {
430 write(bytes, 0, bytes.length);
431 }
432
433 @Override
434 public void write(byte[] bytes, int offset, int length) {
435 byte[] buffer = getBuffer(length);
436
437 System.arraycopy(bytes, offset, buffer, index, length);
438
439 index += length;
440 }
441
442 @Override
443 public void write(int b) {
444 getBuffer(1)[index++] = (byte)b;
445 }
446
447 }
448
449 }