KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > driver > protocol > SQLDataSerialization


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2005 Emic Networks
4  * Contact: c-jdbc@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by the
8  * Free Software Foundation; either version 2.1 of the License, or any later
9  * version.
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19  *
20  * Initial developer(s): Marc Herbert
21  * Contributor(s): ______________________.
22  */

23
24 package org.objectweb.cjdbc.driver.protocol;
25
26 import java.io.IOException JavaDoc;
27 import java.math.BigDecimal JavaDoc;
28 import java.sql.Clob JavaDoc;
29 import java.sql.SQLException JavaDoc;
30 import java.sql.Timestamp JavaDoc;
31
32 import org.objectweb.cjdbc.common.exceptions.NotImplementedException;
33 import org.objectweb.cjdbc.common.stream.CJDBCInputStream;
34 import org.objectweb.cjdbc.common.stream.CJDBCOutputStream;
35
36 /**
37  * This class defines Serializers for SQL Data: per type serialization +
38  * deserialization methods and information wrapped in one object. Serializers
39  * are implemented as singletons for efficiency.
40  *
41  * @author <a HREF="mailto:Marc.Herbert@emicnetworks.com">Marc Herbert </a>
42  * @version 1.0
43  */

44 public final class SQLDataSerialization
45 {
46
47   /**
48    * Unsupported types listed in {@link TypeTag}won't be added in the near
49    * future, except maybe java.net.URL for JDBC 3.0
50    */

51   /**
52    * CLOB support should be easy to base on BLOB implementation once we
53    * figure out the encoding issues.
54    */

55
56   private static final Serializer JAVA_STRING = new StringSerializer();
57   private static final Serializer MATH_BIGDECIMAL = new BigDecimalSerializer();
58   private static final Serializer JAVA_BOOLEAN = new BooleanSerializer();
59   private static final Serializer JAVA_INTEGER = new IntegerSerializer();
60   private static final Serializer JAVA_LONG = new LongSerializer();
61   private static final Serializer JAVA_FLOAT = new FloatSerializer();
62   private static final Serializer JAVA_DOUBLE = new DoubleSerializer();
63   private static final Serializer JAVA_BYTES = new BytesSerializer();
64
65   private static final Serializer SQL_DATE = new DateSerializer(); // UNTESTED
66
private static final Serializer SQL_TIME = new TimeSerializer(); // UNTESTED
67
private static final Serializer SQL_TIMESTAMP = new TimestampSerializer(); // UNTESTED
68

69   // CLOB: TODO
70
private static final Serializer SQL_BLOB = new BlobSerializer();
71   private static final int STREAM_BUF_SIZE = 65536;
72
73   // java.net.URL: TODO
74

75   /** Abstract class hiding type-specific serialization methods and information */
76   public abstract static class Serializer
77   {
78     protected TypeTag typeTag;
79
80     /**
81      * @return the corresponding TypeTag
82      */

83     public TypeTag getTypeTag()
84     {
85       return typeTag;
86     }
87
88     /**
89      * Serialize the object to the stream. Warning: the caller must ensure that
90      * Serializer subtype and Object subtype are compatible.
91      *
92      * @param obj object to send
93      * @param output Output stream
94      * @throws IOException stream error
95      * @throws ClassCastException wrong serializer for this object type
96      */

97
98     public abstract void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
99         throws IOException JavaDoc, ClassCastException JavaDoc;
100
101     /**
102      * De-serialize an object from the stream Warning: the caller must ensure
103      * that Serializer subtype and the incoming object are compatible.
104      *
105      * @param input Input stream
106      * @return the object received from the stream
107      * @throws IOException stream error
108      */

109     public abstract Object JavaDoc receiveFromStream(CJDBCInputStream input)
110         throws IOException JavaDoc;
111
112   }
113
114   /**
115    * Returns the de/serializer appropriate for the given TypeTag, or for the
116    * type of the given SQL object if argument is not a TypeTag (TypeTag already
117    * knows how to serialize itself).
118    *
119    * @param sqlObjOrTypeTag a typetag or a sample SQL object of the type of
120    * interest
121    * @return appropriate serialization + deserialization methods
122    * @throws NotImplementedException if we don't know how to serialize objects
123    * such as the given one (including "null").
124    * @throws IllegalArgumentException if we gave a wrong TypeTag
125    */

126   public static Serializer getSerializer(Object JavaDoc sqlObjOrTypeTag)
127       throws NotImplementedException, IllegalArgumentException JavaDoc
128   {
129     if (null == sqlObjOrTypeTag) // not strictly needed but shorter and safer
130
throw new NotImplementedException(
131           "null has no type, cannot find appropriate serialization");
132     /*
133      * Default values that never match anything: we just need any reference that
134      * is both non-SQL and non-null.
135      */

136     TypeTag tag = TypeTag.CONTROLLER_READY;
137     Object JavaDoc obj = JAVA_STRING;
138
139     /**
140      * Now let's get rid of all these nasty type issues for good by casting once
141      * for all.
142      */

143     if (sqlObjOrTypeTag instanceof TypeTag)
144       tag = (TypeTag) sqlObjOrTypeTag;
145     else
146       obj = sqlObjOrTypeTag;
147
148     /**
149      * THE big switch on (type). "instanceof" is used on the serialization side,
150      * "TypeTag.equals()" is used on the de-serialization side. We could for
151      * performance split this method into two different methods (with the
152      * added burden of keeping them perfectly synchronized)
153      *
154      * @see TypeTag
155      */

156     // STRING
157
if (obj instanceof String JavaDoc || TypeTag.STRING.equals(tag))
158       return JAVA_STRING;
159
160     // BIGDECIMAL
161
if (obj instanceof BigDecimal JavaDoc || TypeTag.BIGDECIMAL.equals(tag))
162       return MATH_BIGDECIMAL;
163
164     // BOOLEAN
165
if (obj instanceof Boolean JavaDoc || TypeTag.BOOLEAN.equals(tag))
166       return JAVA_BOOLEAN;
167
168     // INTEGER
169
if (obj instanceof Integer JavaDoc || TypeTag.INTEGER.equals(tag))
170       return JAVA_INTEGER;
171
172     // LONG
173
if (obj instanceof Long JavaDoc || TypeTag.LONG.equals(tag))
174       return JAVA_LONG;
175
176     // FLOAT
177
if (obj instanceof Float JavaDoc || TypeTag.FLOAT.equals(tag))
178       return JAVA_FLOAT;
179
180     // DOUBLE
181
if (obj instanceof Double JavaDoc || TypeTag.DOUBLE.equals(tag))
182       return JAVA_DOUBLE;
183
184     // BYTE ARRAY
185
if (obj instanceof byte[] || TypeTag.BYTE_ARRAY.equals(tag))
186       return JAVA_BYTES;
187
188     // DATE
189
if (obj instanceof java.sql.Date JavaDoc || TypeTag.SQL_DATE.equals(tag))
190       return SQL_DATE;
191
192     // TIME
193
if (obj instanceof java.sql.Time JavaDoc || TypeTag.SQL_TIME.equals(tag))
194       return SQL_TIME;
195
196     // TIMESTAMP
197
if (obj instanceof Timestamp JavaDoc || TypeTag.SQL_TIMESTAMP.equals(tag))
198       return SQL_TIMESTAMP;
199     
200     // CLOB: TODO
201
if (obj instanceof Clob JavaDoc || TypeTag.CLOB.equals(tag))
202       throw new NotImplementedException(
203           "Clob serialization not yet implemented");
204     
205     // BLOB
206
if (obj instanceof java.sql.Blob JavaDoc || TypeTag.BLOB.equals(tag))
207       return SQL_BLOB;
208
209     // java.net.URL: TODO
210

211     if (sqlObjOrTypeTag instanceof TypeTag)
212       throw new IllegalArgumentException JavaDoc(
213           "Internal error: getSerializer() misused with unknown TypeTag argument:"
214               + tag);
215     else
216       throw new NotImplementedException("Unable to serialize unknown type "
217           + sqlObjOrTypeTag.getClass() + " of object " + sqlObjOrTypeTag);
218   }
219
220   /*
221    * These classes define one serializer per type
222    */

223
224   // STRING
225
private static final class StringSerializer
226       extends Serializer
227   {
228     {
229       typeTag = TypeTag.STRING;
230     }
231
232     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
233         throws IOException JavaDoc
234     {
235       output.writeUTF((String JavaDoc) obj);
236     }
237
238     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
239     {
240       return input.readUTF();
241
242     }
243   }
244
245   // BIGDECIMAL
246
// we serialize this using .toString()
247
private static final class BigDecimalSerializer
248       extends Serializer
249   {
250     {
251       typeTag = TypeTag.BIGDECIMAL;
252     }
253
254     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
255         throws IOException JavaDoc
256     {
257       output.writeUTF(((BigDecimal JavaDoc) obj).toString());
258     }
259
260     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
261     {
262       return new BigDecimal JavaDoc(input.readUTF());
263     }
264   }
265
266   // BOOLEAN
267
private static final class BooleanSerializer
268       extends Serializer
269   {
270     {
271       typeTag = TypeTag.BOOLEAN;
272     }
273
274     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
275         throws IOException JavaDoc
276     {
277       output.writeBoolean(((Boolean JavaDoc) obj).booleanValue());
278     }
279
280     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
281     {
282       return new Boolean JavaDoc(input.readBoolean());
283     }
284   }
285
286   // INTEGER
287
private static final class IntegerSerializer
288       extends Serializer
289   {
290     {
291       typeTag = TypeTag.INTEGER;
292     }
293
294     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
295         throws IOException JavaDoc
296     {
297       /**
298        * let's also accept Short, see PostgreSQL bug explained here
299        *
300        * @see org.objectweb.cjdbc.driver.DriverResultSet#initSerializers()
301        */

302       output.writeInt(((Number JavaDoc) obj).intValue());
303     }
304
305     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
306     {
307       return new Integer JavaDoc(input.readInt());
308     }
309   }
310
311   // LONG
312
private static final class LongSerializer
313       extends Serializer
314   {
315     {
316       typeTag = TypeTag.LONG;
317     }
318
319     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
320         throws IOException JavaDoc
321     {
322       output.writeLong(((Long JavaDoc) obj).longValue());
323     }
324
325     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
326     {
327       return new Long JavaDoc(input.readLong());
328     }
329   }
330
331   // FLOAT
332
private static final class FloatSerializer
333       extends Serializer
334   {
335     {
336       typeTag = TypeTag.FLOAT;
337     }
338
339     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
340         throws IOException JavaDoc
341     {
342       output.writeFloat(((Float JavaDoc) obj).floatValue());
343     }
344
345     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
346     {
347       return new Float JavaDoc(input.readFloat());
348     }
349   }
350
351   // DOUBLE
352
private static final class DoubleSerializer
353       extends Serializer
354   {
355     {
356       typeTag = TypeTag.DOUBLE;
357     }
358
359     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
360         throws IOException JavaDoc
361     {
362       output.writeDouble(((Double JavaDoc) obj).doubleValue());
363     }
364
365     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
366     {
367       return new Double JavaDoc(input.readDouble());
368     }
369   }
370
371   // BYTE ARRAY
372
private static final class BytesSerializer
373       extends Serializer
374   {
375     {
376       typeTag = TypeTag.BYTE_ARRAY;
377     }
378
379     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
380         throws IOException JavaDoc
381     {
382       byte[] b = (byte[]) obj;
383       output.writeInt(b.length);
384       output.write(b);
385     }
386
387     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
388     {
389       int len = input.readInt();
390       byte[] b = new byte[len];
391       input.readFully(b);
392       return b;
393     }
394   }
395
396   // DATE
397
private static final class DateSerializer
398       extends Serializer
399   {
400     {
401       typeTag = TypeTag.SQL_DATE;
402     }
403
404     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
405         throws IOException JavaDoc
406     {
407       output.writeLong(((java.sql.Date JavaDoc) obj).getTime());
408     }
409
410     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
411     {
412       return new java.sql.Date JavaDoc(input.readLong());
413     }
414   }
415
416   // TIME
417
private static final class TimeSerializer
418       extends Serializer
419   {
420     {
421       typeTag = TypeTag.SQL_TIME;
422     }
423
424     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
425         throws IOException JavaDoc
426     {
427       output.writeInt((int) ((java.sql.Time JavaDoc) obj).getTime());
428     }
429
430     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
431     {
432       return new java.sql.Time JavaDoc(input.readInt());
433     }
434   }
435
436   // TIMESTAMP
437
private static final class TimestampSerializer
438       extends Serializer
439   {
440     {
441       typeTag = TypeTag.SQL_TIMESTAMP;
442     }
443
444     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
445         throws IOException JavaDoc
446     {
447       Timestamp JavaDoc ts = (Timestamp JavaDoc) obj;
448       // put the milliseconds trick/CPU load on the driver side
449
output.writeLong(ts.getTime());
450       output.writeInt(ts.getNanos());
451     }
452
453     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
454     {
455       long tsWithMilli = input.readLong();
456       // we don't want the milliseconds twice
457
Timestamp JavaDoc ts = new Timestamp JavaDoc((tsWithMilli / 1000) * 1000);
458       ts.setNanos(input.readInt());
459       return ts;
460     }
461   }
462
463   // CLOB: TODO
464

465   // BLOB
466
private static final class BlobSerializer
467       extends Serializer
468   {
469     {
470       typeTag = TypeTag.BLOB;
471     }
472
473     public void sendToStream(Object JavaDoc obj, CJDBCOutputStream output)
474         throws IOException JavaDoc
475     {
476       java.sql.Blob JavaDoc blob = (java.sql.Blob JavaDoc) obj;
477       try
478       {
479         // Be very careful to be compatible with JAVA_BYTES.sendToStream(),
480
// since we use JAVA_BYTES.receiveFromStream on the other side.
481

482         if (blob.length() > Integer.MAX_VALUE)
483           // FIXME: this is currently corrupting protocol with driver
484
throw new IOException JavaDoc("Blobs bigger than " + Integer.MAX_VALUE
485               + " are not supported");
486
487         // send the size of the byte array
488
output.writeInt((int) blob.length());
489
490         byte[] tempBuffer = new byte[STREAM_BUF_SIZE];
491         java.io.InputStream JavaDoc input = blob.getBinaryStream();
492         int nbRead;
493         while (true)
494         {
495           nbRead = input.read(tempBuffer);
496           if (-1 == nbRead)
497             break;
498           output.write(tempBuffer, 0, nbRead);
499         }
500       }
501       catch (SQLException JavaDoc e)
502       {
503         // Exceptions for Blobs is unfortunately tricky because we can't know in
504
// advance if a java array will be big enough (2^31) to hold them.
505
throw (IOException JavaDoc) new IOException JavaDoc(e.getLocalizedMessage())
506             .initCause(e);
507       }
508     }
509
510     public Object JavaDoc receiveFromStream(CJDBCInputStream input) throws IOException JavaDoc
511     {
512       byte[] b = (byte[]) JAVA_BYTES.receiveFromStream(input);
513       return new org.objectweb.cjdbc.driver.Blob(b);
514     }
515   }
516 }
517
Popular Tags