1 package org.apache.commons.jcs3.engine.behavior;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.EOFException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.nio.ByteBuffer;
27 import java.nio.channels.AsynchronousByteChannel;
28 import java.nio.channels.ReadableByteChannel;
29 import java.nio.channels.WritableByteChannel;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.TimeoutException;
34
35 /**
36 * Defines the behavior for cache element serializers. This layer of abstraction allows us to plug
37 * in different serialization mechanisms, such as a compressing standard serializer.
38 */
39 public interface IElementSerializer
40 {
41 /**
42 * Turns an object into a byte array.
43 *
44 * @param <T> the type of the object
45 * @param obj the object to serialize
46 * @return byte[] a byte array containing the serialized object
47 * @throws IOException if serialization fails
48 */
49 <T> byte[] serialize( T obj )
50 throws IOException;
51
52 /**
53 * Turns a byte array into an object.
54 *
55 * @param bytes data bytes
56 * @param loader class loader to use
57 * @return Object
58 * @throws IOException if de-serialization fails
59 * @throws ClassNotFoundException thrown if we don't know the object.
60 */
61 <T> T deSerialize( byte[] bytes, ClassLoader loader )
62 throws IOException, ClassNotFoundException;
63
64 /**
65 * Convenience method to write serialized object into a stream.
66 * The stream data will be prepended with a four-byte length prefix.
67 *
68 * @param <T> the type of the object
69 * @param obj the object to serialize
70 * @param os the output stream
71 * @return the number of bytes written
72 * @throws IOException if serialization or writing fails
73 * @since 3.1
74 */
75 default <T> int serializeTo(T obj, OutputStream os)
76 throws IOException
77 {
78 final byte[] serialized = serialize(obj);
79 final ByteBuffer buffer = ByteBuffer.allocate(4 + serialized.length);
80 buffer.putInt(serialized.length);
81 buffer.put(serialized);
82 buffer.flip();
83
84 os.write(buffer.array());
85 return buffer.capacity();
86 }
87
88 /**
89 * Convenience method to write serialized object into a channel.
90 * The stream data will be prepended with a four-byte length prefix.
91 *
92 * @param <T> the type of the object
93 * @param obj the object to serialize
94 * @param oc the output channel
95 * @return the number of bytes written
96 * @throws IOException if serialization or writing fails
97 * @since 3.1
98 */
99 default <T> int serializeTo(T obj, WritableByteChannel oc)
100 throws IOException
101 {
102 final byte[] serialized = serialize(obj);
103 final ByteBuffer buffer = ByteBuffer.allocate(4 + serialized.length);
104 buffer.putInt(serialized.length);
105 buffer.put(serialized);
106 buffer.flip();
107
108 int count = 0;
109 while (buffer.hasRemaining())
110 {
111 count += oc.write(buffer);
112 }
113 return count;
114 }
115
116 /**
117 * Convenience method to write serialized object into an
118 * asynchronous channel.
119 * The stream data will be prepended with a four-byte length prefix.
120 *
121 * @param <T> the type of the object
122 * @param obj the object to serialize
123 * @param oc the output channel
124 * @param writeTimeoutMs the write timeout im milliseconds
125 * @return the number of bytes written
126 * @throws IOException if serialization or writing fails
127 * @since 3.1
128 */
129 default <T> int serializeTo(T obj, AsynchronousByteChannel oc, int writeTimeoutMs)
130 throws IOException
131 {
132 final byte[] serialized = serialize(obj);
133 final ByteBuffer buffer = ByteBuffer.allocate(4 + serialized.length);
134 buffer.putInt(serialized.length);
135 buffer.put(serialized);
136 buffer.flip();
137
138 int count = 0;
139 while (buffer.hasRemaining())
140 {
141 Future<Integer> bytesWritten = oc.write(buffer);
142 try
143 {
144 count += bytesWritten.get(writeTimeoutMs, TimeUnit.MILLISECONDS);
145 }
146 catch (InterruptedException | ExecutionException | TimeoutException e)
147 {
148 throw new IOException("Write timeout exceeded " + writeTimeoutMs, e);
149 }
150 }
151
152 return count;
153 }
154
155 /**
156 * Convenience method to read serialized object from a stream.
157 * The method expects to find a four-byte length prefix in the
158 * stream data.
159 *
160 * @param <T> the type of the object
161 * @param is the input stream
162 * @param loader class loader to use
163 * @throws IOException if serialization or reading fails
164 * @throws ClassNotFoundException thrown if we don't know the object.
165 * @since 3.1
166 */
167 default <T> T deSerializeFrom(InputStream is, ClassLoader loader)
168 throws IOException, ClassNotFoundException
169 {
170 final byte[] bufferSize = new byte[4];
171 int read = is.read(bufferSize);
172 if (read < 0)
173 {
174 throw new EOFException("End of stream reached");
175 }
176 assert read == bufferSize.length;
177 ByteBuffer size = ByteBuffer.wrap(bufferSize);
178
179 byte[] serialized = new byte[size.getInt()];
180 read = is.read(serialized);
181 assert read == serialized.length;
182
183 return deSerialize(serialized, loader);
184 }
185
186 /**
187 * Convenience method to read serialized object from a channel.
188 * The method expects to find a four-byte length prefix in the
189 * stream data.
190 *
191 * @param <T> the type of the object
192 * @param ic the input channel
193 * @param loader class loader to use
194 * @throws IOException if serialization or reading fails
195 * @throws ClassNotFoundException thrown if we don't know the object.
196 * @since 3.1
197 */
198 default <T> T deSerializeFrom(ReadableByteChannel ic, ClassLoader loader)
199 throws IOException, ClassNotFoundException
200 {
201 final ByteBuffer bufferSize = ByteBuffer.allocate(4);
202 int read = ic.read(bufferSize);
203 if (read < 0)
204 {
205 throw new EOFException("End of stream reached (length)");
206 }
207 assert read == bufferSize.capacity();
208 bufferSize.flip();
209
210 final ByteBuffer serialized = ByteBuffer.allocate(bufferSize.getInt());
211 while (serialized.remaining() > 0)
212 {
213 read = ic.read(serialized);
214 if (read < 0)
215 {
216 throw new EOFException("End of stream reached (object)");
217 }
218 }
219 serialized.flip();
220
221 return deSerialize(serialized.array(), loader);
222 }
223
224 /**
225 * Convenience method to read serialized object from an
226 * asynchronous channel.
227 * The method expects to find a four-byte length prefix in the
228 * stream data.
229 *
230 * @param <T> the type of the object
231 * @param ic the input channel
232 * @param readTimeoutMs the read timeout in milliseconds
233 * @param loader class loader to use
234 * @throws IOException if serialization or reading fails
235 * @throws ClassNotFoundException thrown if we don't know the object.
236 * @since 3.1
237 */
238 default <T> T deSerializeFrom(AsynchronousByteChannel ic, int readTimeoutMs, ClassLoader loader)
239 throws IOException, ClassNotFoundException
240 {
241 final ByteBuffer bufferSize = ByteBuffer.allocate(4);
242 Future<Integer> readFuture = ic.read(bufferSize);
243
244 try
245 {
246 int read = readFuture.get(readTimeoutMs, TimeUnit.MILLISECONDS);
247 if (read < 0)
248 {
249 throw new EOFException("End of stream reached (length)");
250 }
251 assert read == bufferSize.capacity();
252 }
253 catch (InterruptedException | ExecutionException | TimeoutException e)
254 {
255 throw new IOException("Read timeout exceeded (length)" + readTimeoutMs, e);
256 }
257
258 bufferSize.flip();
259
260 final ByteBuffer serialized = ByteBuffer.allocate(bufferSize.getInt());
261 while (serialized.remaining() > 0)
262 {
263 readFuture = ic.read(serialized);
264 try
265 {
266 int read = readFuture.get(readTimeoutMs, TimeUnit.MILLISECONDS);
267 if (read < 0)
268 {
269 throw new EOFException("End of stream reached (object)");
270 }
271 }
272 catch (InterruptedException | ExecutionException | TimeoutException e)
273 {
274 throw new IOException("Read timeout exceeded (object)" + readTimeoutMs, e);
275 }
276 }
277
278 serialized.flip();
279
280 return deSerialize(serialized.array(), loader);
281 }
282 }