001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.io;
016    
017    import com.liferay.portal.kernel.util.CharBufferPool;
018    import com.liferay.portal.kernel.util.ClassLoaderPool;
019    import com.liferay.portal.kernel.util.ClassResolverUtil;
020    
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.ObjectInputStream;
024    import java.io.Serializable;
025    
026    import java.nio.ByteBuffer;
027    
028    /**
029     * Deserializes data in a ClassLoader-aware manner. This class is the
030     * counterpart of {@link Serializer} for deserialization.
031     *
032     * @author Shuyang Zhou
033     * @see    Serializer
034     */
035    public class Deserializer {
036    
037            public Deserializer(ByteBuffer byteBuffer) {
038                    buffer = byteBuffer.array();
039                    index = byteBuffer.arrayOffset();
040                    limit = index + byteBuffer.remaining();
041            }
042    
043            public boolean readBoolean() {
044                    detectBufferUnderflow(1);
045    
046                    return BigEndianCodec.getBoolean(buffer, index++);
047            }
048    
049            public byte readByte() {
050                    detectBufferUnderflow(1);
051    
052                    return buffer[index++];
053            }
054    
055            public char readChar() {
056                    detectBufferUnderflow(2);
057    
058                    char c = BigEndianCodec.getChar(buffer, index);
059    
060                    index += 2;
061    
062                    return c;
063            }
064    
065            public double readDouble() {
066                    detectBufferUnderflow(8);
067    
068                    double d = BigEndianCodec.getDouble(buffer, index);
069    
070                    index += 8;
071    
072                    return d;
073            }
074    
075            public float readFloat() {
076                    detectBufferUnderflow(4);
077    
078                    float f = BigEndianCodec.getFloat(buffer, index);
079    
080                    index += 4;
081    
082                    return f;
083            }
084    
085            public int readInt() {
086                    detectBufferUnderflow(4);
087    
088                    int i = BigEndianCodec.getInt(buffer, index);
089    
090                    index += 4;
091    
092                    return i;
093            }
094    
095            public long readLong() {
096                    detectBufferUnderflow(8);
097    
098                    long l = BigEndianCodec.getLong(buffer, index);
099    
100                    index += 8;
101    
102                    return l;
103            }
104    
105            public <T extends Serializable> T readObject()
106                    throws ClassNotFoundException {
107    
108                    byte tcByte = buffer[index++];
109    
110                    switch (tcByte) {
111                            case SerializationConstants.TC_BOOLEAN:
112                                    return (T)Boolean.valueOf(readBoolean());
113    
114                            case SerializationConstants.TC_BYTE:
115                                    return (T)Byte.valueOf(readByte());
116    
117                            case SerializationConstants.TC_CHARACTER:
118                                    return (T)Character.valueOf(readChar());
119    
120                            case SerializationConstants.TC_CLASS:
121                                    String contextName = readString();
122                                    String className = readString();
123    
124                                    ClassLoader classLoader = ClassLoaderPool.getClassLoader(
125                                            contextName);
126    
127                                    return (T)ClassResolverUtil.resolve(className, classLoader);
128    
129                            case SerializationConstants.TC_DOUBLE:
130                                    return (T)Double.valueOf(readDouble());
131    
132                            case SerializationConstants.TC_FLOAT:
133                                    return (T)Float.valueOf(readFloat());
134    
135                            case SerializationConstants.TC_INTEGER:
136                                    return (T)Integer.valueOf(readInt());
137    
138                            case SerializationConstants.TC_LONG:
139                                    return (T)Long.valueOf(readLong());
140    
141                            case SerializationConstants.TC_NULL:
142                                    return null;
143    
144                            case SerializationConstants.TC_SHORT:
145                                    return (T)Short.valueOf(readShort());
146    
147                            case SerializationConstants.TC_STRING:
148                                    return (T)readString();
149    
150                            case SerializationConstants.TC_OBJECT:
151                                    try {
152                                            ObjectInputStream objectInpputStream =
153                                                    new AnnotatedObjectInputStream(new BufferInputStream());
154    
155                                            T t = (T)objectInpputStream.readObject();
156    
157                                            objectInpputStream.close();
158    
159                                            return t;
160                                    }
161                                    catch (IOException ioe) {
162                                            throw new RuntimeException(ioe);
163                                    }
164    
165                            default :
166                                    throw new IllegalStateException("Unkown TC code " + tcByte);
167                    }
168            }
169    
170            public short readShort() {
171                    detectBufferUnderflow(2);
172    
173                    short s = BigEndianCodec.getShort(buffer, index);
174    
175                    index += 2;
176    
177                    return s;
178            }
179    
180            public String readString() {
181                    detectBufferUnderflow(5);
182    
183                    boolean asciiCode = BigEndianCodec.getBoolean(buffer, index++);
184    
185                    int length = BigEndianCodec.getInt(buffer, index);
186    
187                    index += 4;
188    
189                    if (asciiCode) {
190                            detectBufferUnderflow(length);
191    
192                            char[] chars = CharBufferPool.borrow(length);
193    
194                            for (int i = 0; i < length; i++) {
195                                    chars[i] = (char)buffer[index++];
196                            }
197    
198                            return new String(chars, 0, length);
199                    }
200                    else {
201                            detectBufferUnderflow(length * 2);
202    
203                            char[] chars = CharBufferPool.borrow(length);
204    
205                            for (int i = 0; i < length; i++) {
206                                    chars[i] = BigEndianCodec.getChar(buffer, index);
207    
208                                    index += 2;
209                            }
210    
211                            return new String(chars, 0, length);
212                    }
213            }
214    
215            /**
216             * Detects a buffer underflow throwing an {@link
217             * java.lang.IllegalStateException} if the input data is shorter than the
218             * reserved space. This method is final so JIT can perform an inline
219             * expansion.
220             *
221             * @param availableBytes number of bytes available in input buffer
222             */
223            protected final void detectBufferUnderflow(int availableBytes) {
224                    if ((index + availableBytes) > limit) {
225                            throw new IllegalStateException("Buffer underflow");
226                    }
227            }
228    
229            protected byte[] buffer;
230            protected int index;
231            protected int limit;
232    
233            protected class BufferInputStream extends InputStream {
234    
235                    @Override
236                    public int read() {
237                            return buffer[index++];
238                    }
239    
240                    @Override
241                    public int read(byte[] bytes) {
242                            return read(bytes, 0, bytes.length);
243                    }
244    
245                    @Override
246                    public int read(byte[] bytes, int offset, int length) {
247                            int remain = limit - index;
248    
249                            if (remain < length) {
250                                    length = remain;
251                            }
252    
253                            System.arraycopy(buffer, index, bytes, offset, length);
254    
255                            index += length;
256    
257                            return length;
258                    }
259    
260            }
261    
262    }