KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > exoplatform > services > database > impl > TextClobType


1 /***************************************************************************
2  * Copyright 2001-2003 The eXo Platform SARL All rights reserved. *
3  * Please look at license.txt in info directory for more license detail. *
4  **************************************************************************/

5
6 package org.exoplatform.services.database.impl;
7
8 import java.io.IOException JavaDoc;
9 import java.io.OutputStream JavaDoc;
10 import java.io.Reader JavaDoc;
11 import java.io.StringReader JavaDoc;
12 import java.lang.reflect.Field JavaDoc;
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.lang.reflect.Method JavaDoc;
15 import java.sql.Clob JavaDoc;
16 import java.sql.Connection JavaDoc;
17 import java.sql.DatabaseMetaData JavaDoc;
18 import java.sql.PreparedStatement JavaDoc;
19 import java.sql.ResultSet JavaDoc;
20 import java.sql.SQLException JavaDoc;
21 import java.sql.Statement JavaDoc;
22
23 import java.sql.Types JavaDoc;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import net.sf.hibernate.HibernateException;
29 import net.sf.hibernate.UserType;
30
31 /**
32  * Created by The eXo Platform SARL . Author : Travis Gregg Date: Oct 25, 2004
33  * Time: 1:12:22 PM <br>
34  * Custom Hibernate type to be used instead of the built in type 'TEXT'. This
35  * custom type was created to handle Oracle Clobs differently. Using the 'TEXT'
36  * type, Oracle Clobs can not participate in transactions, nor can they be
37  * pulled into batches. For some reason, handling them this way, using oracle
38  * temporaries gets around this. <BR>
39  * <BR>
40  * Thanks to reflection (and code on the Hibernate forum) this class can be
41  * compiled without having the oracle drivers (classes12.zip) in your classpath.
42  * <br>
43  * This is based on some code found in the Hibernate user forum:
44  * http://www.hibernate.org/56.html <br>
45  * http://www.hibernate.org/73.html
46  *
47  * @author tgregg
48  */

49 public class TextClobType implements UserType {
50   private static final Log logger = LogFactory.getLog(TextClobType.class);
51
52   /**
53    * Name of the oracle driver -- used to support Oracle clobs as a special case
54    */

55   private static final String JavaDoc ORACLE_DRIVER_NAME = "Oracle JDBC driver";
56
57   /** Version of the oracle driver being supported with clob. */
58   private static final int ORACLE_DRIVER_MAJOR_VERSION = 9;
59
60   private static final int ORACLE_DRIVER_MINOR_VERSION = 0;
61
62   public TextClobType() {
63   }
64
65   public int[] sqlTypes() {
66     return new int[] { Types.CLOB };
67   }
68
69   public Class JavaDoc returnedClass() {
70     return String JavaDoc.class;
71   }
72
73   public boolean equals(Object JavaDoc x, Object JavaDoc y) throws HibernateException {
74     return (x == y) || (x != null && x.equals(y));
75   }
76
77   public void nullSafeSet(PreparedStatement JavaDoc stmt, Object JavaDoc value, int index)
78       throws HibernateException, SQLException JavaDoc {
79
80     // if this is a PreparedStatement wrapper, get the underlying
81
// PreparedStatement
82
PreparedStatement JavaDoc realStatement = getRealStatement(stmt);
83
84     DatabaseMetaData JavaDoc dbMetaData = realStatement.getConnection().getMetaData();
85
86     if (value == null) {
87       stmt.setNull(index, sqlTypes()[0]);
88     } else if (ORACLE_DRIVER_NAME.equals(dbMetaData.getDriverName())) {
89       if ((dbMetaData.getDriverMajorVersion() >= ORACLE_DRIVER_MAJOR_VERSION)
90           && (dbMetaData.getDriverMinorVersion() >= ORACLE_DRIVER_MINOR_VERSION)) {
91         try {
92           // Code compliments of Scott Miller
93
// support oracle clobs without requiring oracle libraries
94
// at compile time
95
// Note this assumes that if you are using the Oracle
96
// Driver.
97
// then you have access to the oracle.sql.CLOB class
98

99           // First get the oracle clob class
100
Class JavaDoc oracleClobClass = Class.forName("oracle.sql.CLOB");
101
102           // Get the oracle connection class for checking
103
Class JavaDoc oracleConnectionClass = Class
104               .forName("oracle.jdbc.OracleConnection");
105
106           // now get the static factory method
107
Class JavaDoc partypes[] = new Class JavaDoc[3];
108           partypes[0] = Connection JavaDoc.class;
109           partypes[1] = Boolean.TYPE;
110           partypes[2] = Integer.TYPE;
111           Method JavaDoc createTemporaryMethod = oracleClobClass.getDeclaredMethod(
112               "createTemporary", partypes);
113           // now get ready to call the factory method
114
Field JavaDoc durationSessionField = oracleClobClass
115               .getField("DURATION_SESSION");
116           Object JavaDoc arglist[] = new Object JavaDoc[3];
117           Connection JavaDoc conn = realStatement.getConnection();
118
119           // Make sure connection object is right type
120
if (!oracleConnectionClass.isAssignableFrom(conn.getClass())) {
121             throw new HibernateException(
122                 "JDBC connection object must be a oracle.jdbc.OracleConnection. "
123                     + "Connection class is " + conn.getClass().getName());
124           }
125
126           arglist[0] = conn;
127           arglist[1] = Boolean.TRUE;
128           arglist[2] = durationSessionField.get(null); //null is
129
// valid
130
// because of
131
// static field
132

133           // Create our CLOB
134
Object JavaDoc tempClob = createTemporaryMethod.invoke(null, arglist); //null
135
// is
136
// valid
137
// because
138
// of
139
// static
140
// method
141

142           // get the open method
143
partypes = new Class JavaDoc[1];
144           partypes[0] = Integer.TYPE;
145           Method JavaDoc openMethod = oracleClobClass.getDeclaredMethod("open",
146               partypes);
147
148           // prepare to call the method
149
Field JavaDoc modeReadWriteField = oracleClobClass.getField("MODE_READWRITE");
150           arglist = new Object JavaDoc[1];
151           arglist[0] = modeReadWriteField.get(null); //null is valid
152
// because of
153
// static field
154

155           // call open(CLOB.MODE_READWRITE);
156
openMethod.invoke(tempClob, arglist);
157
158           // get the getCharacterOutputStream method
159
//Method getCharacterOutputStreamMethod =
160
// oracleClobClass.getDeclaredMethod(
161
// "getCharacterOutputStream", null );
162

163           // use getAsciiOutputStream for special characters,
164
// using the Writer obtained from 'getCharacterOutputStream"
165
// causes
166
// 'No more data to read from socket' when inserting special
167
// characters
168
Method JavaDoc getAsciiOutputStreamMethod = oracleClobClass
169               .getDeclaredMethod("getAsciiOutputStream", null);
170
171           // call the getCharacterOutpitStream method
172
OutputStream JavaDoc tempClobOutputStream = (OutputStream JavaDoc) getAsciiOutputStreamMethod
173               .invoke(tempClob, null);
174
175           // write the string to the clob
176
tempClobOutputStream.write(((String JavaDoc) value).getBytes());
177           tempClobOutputStream.flush();
178           tempClobOutputStream.close();
179
180           // get the close method
181
Method JavaDoc closeMethod = oracleClobClass.getDeclaredMethod("close", null);
182
183           // call the close method
184
closeMethod.invoke(tempClob, null);
185
186           // add the clob to the statement
187
realStatement.setClob(index, (Clob JavaDoc) tempClob);
188         } catch (ClassNotFoundException JavaDoc e) {
189           // could not find the class with reflection
190
throw new HibernateException("Unable to find a required class.\n"
191               + e.getMessage());
192         } catch (NoSuchMethodException JavaDoc e) {
193           // could not find the metho with reflection
194
throw new HibernateException("Unable to find a required method.\n"
195               + e.getMessage());
196         } catch (NoSuchFieldException JavaDoc e) {
197           // could not find the field with reflection
198
throw new HibernateException("Unable to find a required field.\n"
199               + e.getMessage());
200         } catch (IllegalAccessException JavaDoc e) {
201           throw new HibernateException(
202               "Unable to access a required method or field.\n" + e.getMessage());
203         } catch (InvocationTargetException JavaDoc e) {
204           throw new HibernateException(e.getMessage());
205         } catch (IOException JavaDoc e) {
206           throw new HibernateException(e.getMessage());
207         }
208       } else {
209         throw new HibernateException("No CLOBS support. Use driver version "
210             + ORACLE_DRIVER_MAJOR_VERSION + ", minor "
211             + ORACLE_DRIVER_MINOR_VERSION);
212       }
213     } else {
214       // this is the default way to handle Clobs that seems to work with
215
// most Databases
216
String JavaDoc str = (String JavaDoc) value;
217       StringReader JavaDoc r = new StringReader JavaDoc(str);
218       stmt.setCharacterStream(index, r, str.length());
219     }
220   }
221
222   /**
223    * This method tries to determine if the passed PreparedStatement is a Wrapper
224    * for an actual PreparedStatement. Database objects are often wrapped in
225    * pooling implementations to handle connection clean up, and EJB type
226    * transaction participation. We need to get at the real PreparedStatement to
227    * determin if it is an Oracle PreparedStatement that is being wrapped. This
228    * allows us to handle Oracle differently, since Oracle LOB types work
229    * differently in JDBC than all other databases.
230    *
231    * @param stmt
232    * @return The passed statement, or the PreparedStatement that the passed stmt
233    * is wrapping.
234    * @throws HibernateException
235    */

236   PreparedStatement JavaDoc getRealStatement(PreparedStatement JavaDoc stmt)
237       throws HibernateException {
238     Method JavaDoc[] methods = stmt.getClass().getMethods();
239     for (int i = 0; i < methods.length; i++) {
240       String JavaDoc returnType = methods[i].getReturnType().getName();
241
242       // if the method has no parameters and the return type is either
243
// Statement or PreparedStatement
244
// then reflectively call this method to get the underlying
245
// PreparedStatement
246
// (JBoss returns a Statement that we must cast)
247
if (((Statement JavaDoc.class.getName().equals(returnType))
248           || (PreparedStatement JavaDoc.class.getName().equals(returnType)))
249           && methods[i].getParameterTypes().length == 0) {
250         Statement JavaDoc s = null;
251         try {
252           s = (Statement JavaDoc) methods[i].invoke(stmt, null);
253         } catch (SecurityException JavaDoc e) {
254           throw new HibernateException(
255               "Security Error getting method [getDelegate] on ["
256                   + stmt.getClass().getName() + "::" + methods[i].getName()
257                   + "]", e);
258         } catch (IllegalArgumentException JavaDoc e) {
259           throw new HibernateException(
260               "Error calling method [getDelegate] on ["
261                   + stmt.getClass().getName() + "::" + methods[i].getName()
262                   + "]", e);
263         } catch (IllegalAccessException JavaDoc e) {
264           throw new HibernateException(
265               "Error calling method [getDelegate] on ["
266                   + stmt.getClass().getName() + "::" + methods[i].getName()
267                   + "]", e);
268         } catch (InvocationTargetException JavaDoc e) {
269           throw new HibernateException(
270               "Error calling method [getDelegate] on ["
271                   + stmt.getClass().getName() + "::" + methods[i].getName()
272                   + "]", e);
273         }
274         return (PreparedStatement JavaDoc) s;
275
276       }
277     }
278
279     // Did not find a parameterless method that returned a Statement, return
280
// what we were passed
281
return stmt;
282
283   }
284
285   /**
286    * @throws HibernateException
287    * @see net.sf.hibernate.UserType#deepCopy(java.lang.Object)
288    */

289   public Object JavaDoc deepCopy(Object JavaDoc value) throws HibernateException {
290     if (value == null) {
291       return null;
292     } else {
293       String JavaDoc stringValue = (String JavaDoc) value;
294       return new String JavaDoc(stringValue);
295     }
296   }
297
298   /**
299    * @see net.sf.hibernate.UserType#isMutable()
300    */

301   public boolean isMutable() {
302     return false;
303   }
304
305   /**
306    * Impl copied from net.sf.hibernate.type.TextType Generated 10:08:31 AM May
307    * 21, 2004
308    *
309    * @param rs
310    * @param names
311    * @param owner
312    * @return
313    * @throws HibernateException
314    * @throws SQLException
315    * @see net.sf.hibernate.UserType#nullSafeGet(java.sql.ResultSet,
316    * java.lang.String[], java.lang.Object)
317    */

318   public Object JavaDoc nullSafeGet(ResultSet JavaDoc rs, String JavaDoc[] names, Object JavaDoc owner)
319       throws HibernateException, SQLException JavaDoc {
320     // Retrieve the value of the designated column in the current row of
321
// this
322
// ResultSet object as a java.io.Reader object
323
Reader JavaDoc charReader = rs.getCharacterStream(names[0]);
324
325     // if the corresponding SQL value is NULL, the reader we got is NULL as
326
// well
327
if (charReader == null)
328       return null;
329
330     // Fetch Reader content up to the end - and put characters in a
331
// StringBuffer
332
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
333     try {
334       char[] buffer = new char[2048];
335       while (true) {
336         int amountRead = charReader.read(buffer, 0, buffer.length);
337         if (amountRead == -1)
338           break;
339         sb.append(buffer, 0, amountRead);
340       }
341     } catch (IOException JavaDoc ioe) {
342       throw new HibernateException("IOException occurred reading text", ioe);
343     } finally {
344       try {
345         charReader.close();
346       } catch (IOException JavaDoc e) {
347         throw new HibernateException("IOException occurred closing stream", e);
348       }
349     }
350
351     // Return StringBuffer content as a large String
352
return sb.toString();
353   }
354 }
Popular Tags