KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > loader > JDBCCacheLoader


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.cache.loader;
8
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.jboss.cache.Fqn;
12 import org.jboss.cache.Modification;
13 import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
14
15 import javax.naming.InitialContext JavaDoc;
16 import javax.naming.NamingException JavaDoc;
17 import javax.sql.DataSource JavaDoc;
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.ByteArrayOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.ObjectInputStream JavaDoc;
23 import java.io.ObjectOutputStream JavaDoc;
24 import java.sql.Connection JavaDoc;
25 import java.sql.DatabaseMetaData JavaDoc;
26 import java.sql.DriverManager JavaDoc;
27 import java.sql.PreparedStatement JavaDoc;
28 import java.sql.ResultSet JavaDoc;
29 import java.sql.SQLException JavaDoc;
30 import java.sql.Statement JavaDoc;
31 import java.sql.Types JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Collection JavaDoc;
34 import java.util.Collections JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Properties JavaDoc;
40 import java.util.Set JavaDoc;
41
42 /**
43  * JDBC CacheLoader implementation.
44  * <p/>
45  * This implementation uses one table. The table consists of three columns:
46  * <ul>
47  * <li>text column for fqn (which is also a primary key)</li>
48  * <li>blob column for attributes (can contain null)</li>
49  * <li>text column for parent fqn (can contain null)</li>
50  * </ul>
51  * <p/>
52  * The configuration options are:
53  * <p/>
54  * <b>Table configuration</b>
55  * <ul>
56  * <li><b>cache.jdbc.table.name</b> - the table name (default is <i>jbosscache</i>)</li>
57  * <li><b>cache.jdbc.table.create</b> - should be true or false, indicates whether to create the table at start phase</li>
58  * <li><b>cache.jdbc.table.drop</b> - should be true or false, indicates whether to drop the table at stop phase</li>
59  * <li><b>cache.jdbc.table.primarykey</b> - the name for the table primary key (default is <i>jbosscache_pk</i>)</li>
60  * <li><b>cache.jdbc.fqn.column</b> - the name for the fqn column (default is <i>fqn</i>)</li>
61  * <li><b>cache.jdbc.fqn.type</b> - the type for the fqn column (default is <i>varchar(255)</i>)</li>
62  * <li><b>cache.jdbc.node.column</b> - the name for the node's contents column (default is <i>node</i>)</li>
63  * <li><b>cache.jdbc.node.type</b> - the type for the node's contents column (default is <i>blob</i>)</li>
64  * <li><b>cache.jdbc.parent.column</b> - the name for the parent fqn column (default is <i>parent</i>)</li>
65  * </ul>
66  * <p/>
67  * <b>DataSource configuration</b>
68  * <ul>
69  * <li><b>cache.jdbc.datasource</b> - the JNDI name of the datasource</li>
70  * </ul>
71  * <p/>
72  * <b>JDBC driver configuration (used when DataSource is not configured)</b>
73  * <ul>
74  * <li><b>cache.jdbc.driver</b> - fully qualified JDBC driver name</li>
75  * <li><b>cache.jdbc.url</b> - URL to connect to the database</li>
76  * <li><b>cache.jdbc.user</b> - the username to use to connect to the database</li>
77  * <li><b>cache.jdbc.password</b> - the password to use to connect to the database</li>
78  * </ul>
79  *
80  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
81  * @author <a HREF="mailto:hmesha@novell.com">Hany Mesha </a>
82  * @version <tt>$Revision: 1.26 $</tt>
83  */

84 public class JDBCCacheLoader extends AbstractCacheLoader
85 {
86    private static final Log log = LogFactory.getLog(JDBCCacheLoader.class);
87
88    private static final ThreadLocal JavaDoc connection = new ThreadLocal JavaDoc();
89
90    private JDBCCacheLoaderConfig config;
91    private ConnectionFactory cf;
92    private String JavaDoc driverName;
93
94    public void setConfig(IndividualCacheLoaderConfig base)
95    {
96       if (config instanceof JDBCCacheLoaderConfig)
97       {
98          config = (JDBCCacheLoaderConfig) base;
99       }
100       else
101       {
102          config = new JDBCCacheLoaderConfig(base);
103       }
104
105       if (config.getDatasourceName() == null)
106       {
107          this.cf = new NonManagedConnectionFactory(config.getJdbcURL(), config.getJdbcUser(), config.getJdbcPassword());
108       }
109       // else we wait until the start method to do a JNDI lookup
110
// of the datasource, since that's when its registered in its lifecycle
111
}
112
113    public IndividualCacheLoaderConfig getConfig()
114    {
115       return config;
116    }
117
118    /**
119     * Fetches child node names (not pathes).
120     *
121     * @param fqn parent fqn
122     * @return a set of child node names or null if there are not children found for the fqn
123     * @throws Exception
124     */

125    public Set JavaDoc<String JavaDoc> getChildrenNames(Fqn fqn) throws Exception JavaDoc
126    {
127       Set JavaDoc children = null;
128       Connection JavaDoc con = null;
129       PreparedStatement JavaDoc ps = null;
130       ResultSet JavaDoc rs = null;
131       try
132       {
133          if (log.isDebugEnabled())
134          {
135             log.debug("executing sql: " + config.getSelectChildNamesSql() + " (" + fqn + ")");
136          }
137
138          con = cf.getConnection();
139          ps = con.prepareStatement(config.getSelectChildNamesSql());
140          ps.setString(1, fqn.toString());
141          rs = ps.executeQuery();
142          if (rs.next())
143          {
144             children = new HashSet JavaDoc();
145             do
146             {
147                String JavaDoc child = rs.getString(1);
148                int slashInd = child.lastIndexOf('/');
149                String JavaDoc name = child.substring(slashInd + 1);
150                //Fqn childFqn = Fqn.fromString(child);
151
//String name = (String) childFqn.get(childFqn.size() - 1);
152
children.add(name);
153             }
154             while (rs.next());
155          }
156       }
157       catch (SQLException JavaDoc e)
158       {
159          log.error("Failed to get children names for fqn " + fqn, e);
160          throw new IllegalStateException JavaDoc("Failed to get children names for fqn " + fqn + ": " + e.getMessage());
161       }
162       finally
163       {
164          safeClose(rs);
165          safeClose(ps);
166          cf.close(con);
167       }
168
169       return children == null ? null : Collections.unmodifiableSet(children);
170    }
171
172    // See http://jira.jboss.com/jira/browse/JBCACHE-118 for why this is commented out.
173

174    /**
175     * Loads an attribute from the database.
176     *
177     * @param name node's fqn
178     * @param key attribute's key
179     * @return attribute's value. Null is returned if
180     * <ul>
181     * <li>there is no value for the attribute key in the node</li>
182     * <li>there is a row in the table for the fqn but the node column contains null</li>
183     * <li>there is no row in table for the fqn (should this result in an exception?)</li>
184     * </ul>
185     * @throws Exception
186     */

187    // public Object get(Fqn name, Object key) throws Exception
188
// {
189
// Map node = loadNode(name);
190
// return node == null || node == NULL_NODE_IN_ROW ? null : node.get(key);
191
// }
192

193    /**
194     * Returns a map representing a node.
195     *
196     * @param name node's fqn
197     * @return node
198     * @throws Exception
199     */

200    public Map JavaDoc get(Fqn name) throws Exception JavaDoc
201    {
202       final Map JavaDoc node = loadNode(name);
203       return node == NULL_NODE_IN_ROW ? new HashMap JavaDoc(0) : node;
204    }
205
206    /**
207     * Checks that there is a row for the fqn in the database.
208     *
209     * @param name node's fqn
210     * @return true if there is a row in the database for the given fqn even if the node column is null.
211     * @throws Exception
212     */

213    public boolean exists(Fqn name) throws Exception JavaDoc
214    {
215       final Map JavaDoc node = loadNode(name);
216       return node != null;// && node != NULL_NODE_IN_ROW;
217
}
218
219    /**
220     * Adds/overrides a value in a node for a key.
221     * If the node does not exist yet, the node will be created.
222     * If parent nodes do not exist for the node, empty parent nodes will be created.
223     *
224     * @param name node's fqn
225     * @param key attribute's key
226     * @param value attribute's value
227     * @return old value associated with the attribute's key or null if there was no value previously
228     * associated with the attribute's key
229     * @throws Exception
230     */

231    public Object JavaDoc put(Fqn name, Object JavaDoc key, Object JavaDoc value) throws Exception JavaDoc
232    {
233       Map JavaDoc oldNode = loadNode(name);
234       Object JavaDoc oldValue;
235       Map JavaDoc node;
236
237       if (oldNode == null || oldNode == NULL_NODE_IN_ROW)
238       {
239          node = new HashMap JavaDoc();
240       }
241       else
242       {
243          node = oldNode;
244       }
245       oldValue = node.put(key, value);
246
247       if (oldNode != null)
248       {
249          updateNode(name, node);
250       }
251       else
252       {
253          if (name.size() > 1)
254          {
255             for (int i = 1; i < name.size(); ++i)
256             {
257                final Fqn parent = name.getFqnChild(i);
258                if (!exists(parent))
259                {
260                   insertNode(parent, null);
261                }
262             }
263          }
264          insertNode(name, node);
265       }
266
267       return oldValue;
268    }
269
270    /**
271     * Adds attributes from the passed in map to the existing node.
272     * If there is no node for the fqn, a new node will be created.
273     *
274     * @param name node's fqn
275     * @param attributes attributes
276     * @throws Exception
277     */

278    public void put(Fqn name, Map JavaDoc attributes) throws Exception JavaDoc
279    {
280       put(name, attributes, false);
281    }
282
283    /**
284     * Removes attribute's value for a key. If after removal the node contains no attributes, the node is nullified.
285     *
286     * @param name node's name
287     * @param key attribute's key
288     * @return removed value or null if there was no value for the passed in key
289     * @throws Exception
290     */

291    public Object JavaDoc remove(Fqn name, Object JavaDoc key) throws Exception JavaDoc
292    {
293       Object JavaDoc removedValue = null;
294       Map JavaDoc node = loadNode(name);
295       if (node != null && node != NULL_NODE_IN_ROW)
296       {
297          removedValue = node.remove(key);
298          if (node.isEmpty())
299          {
300             updateNode(name, null);
301          }
302          else
303          {
304             updateNode(name, node);
305          }
306       }
307       return removedValue;
308    }
309
310    /**
311     * Removes a node and all its children.
312     * Uses the same connection for all the db work.
313     *
314     * @param name node's fqn
315     * @throws Exception
316     */

317    public void remove(Fqn name) throws Exception JavaDoc
318    {
319       Connection JavaDoc con = null;
320       PreparedStatement JavaDoc ps = null;
321       try
322       {
323          if (name.size() == 0)
324          {
325             if (log.isDebugEnabled())
326             {
327                log.debug("executing sql: " + config.getDeleteAllSql());
328             }
329
330             con = cf.getConnection();
331             ps = con.prepareStatement(config.getDeleteAllSql());
332             int deletedRows = ps.executeUpdate();
333
334             if (log.isDebugEnabled())
335             {
336                log.debug("total rows deleted: " + deletedRows);
337             }
338          }
339          else
340          {
341             StringBuffer JavaDoc sql = new StringBuffer JavaDoc(300);
342             sql.append("delete from ").append(config.getTable()).append(" where fqn in (");
343             //sql2.append("delete from " + table + " where fqn=? or parent in (");
344
List JavaDoc fqns = new ArrayList JavaDoc();
345
346             addChildrenToDeleteSql(name.toString(), sql, fqns);
347
348             sql.append(')');
349
350             if (fqns.size() == 1)
351             {
352                if (log.isDebugEnabled())
353                {
354                   log.debug("executing sql: " + config.getDeleteNodeSql() + "(" + name + ")");
355                }
356
357                con = cf.getConnection();
358                ps = con.prepareStatement(config.getDeleteNodeSql());
359                ps.setString(1, name.toString());
360             }
361             else
362             {
363                if (log.isDebugEnabled())
364                {
365                   log.debug("executing sql: " + sql + " " + fqns);
366                }
367
368                con = cf.getConnection();
369                ps = con.prepareStatement(sql.toString());
370                for (int i = 0; i < fqns.size(); ++i)
371                {
372                   ps.setString(i + 1, (String JavaDoc) fqns.get(i));
373                }
374             }
375
376             int deletedRows = ps.executeUpdate();
377
378             if (log.isDebugEnabled())
379             {
380                log.debug("total rows deleted: " + deletedRows);
381             }
382          }
383       }
384       catch (SQLException JavaDoc e)
385       {
386          log.error("Failed to remove node " + name, e);
387          throw new IllegalStateException JavaDoc("Failed to remove node " + name + ": " + e.getMessage());
388       }
389       finally
390       {
391          safeClose(ps);
392          cf.close(con);
393       }
394    }
395
396    /**
397     * Nullifies the node.
398     *
399     * @param name node's fqn
400     * @throws Exception
401     */

402    public void removeData(Fqn name) throws Exception JavaDoc
403    {
404       updateNode(name, null);
405    }
406
407    /**
408     * First phase in transaction commit process. The changes are committed if only one phase if requested.
409     * All the modifications are committed using the same connection.
410     *
411     * @param tx something representing transaction
412     * @param modifications a list of modifications
413     * @param one_phase indicates whether it's one or two phase commit transaction
414     * @throws Exception
415     */

416    public void prepare(Object JavaDoc tx, List JavaDoc<Modification> modifications, boolean one_phase) throws Exception JavaDoc
417    {
418       // start a tx
419
//JBCACHE-346 fix, we don't need to prepare a DataSource object (Managed connection)
420
if (cf instanceof NonManagedConnectionFactory)
421       {
422          Connection JavaDoc con = cf.prepare(tx);
423          if (log.isTraceEnabled())
424          {
425             log.trace("openned tx connection: tx=" + tx + ", con=" + con);
426          }
427       }
428
429       try
430       {
431          put(modifications);
432
433          // commit if it's one phase only
434
if (one_phase)
435          {
436             commit(tx);
437          }
438       }
439       catch (Exception JavaDoc e)
440       {
441          // todo should I rollback it here or rollback is supposed to be invoke by someone from outside?
442
rollback(tx);
443          // is this ok?
444
throw e;
445       }
446    }
447
448    /**
449     * Commits a transaction.
450     *
451     * @param tx the tx to commit
452     * @throws Exception
453     */

454    public void commit(Object JavaDoc tx) throws Exception JavaDoc
455    {
456       cf.commit(tx);
457    }
458
459    /**
460     * Rolls back a transaction.
461     *
462     * @param tx the tx to rollback
463     */

464    public void rollback(Object JavaDoc tx)
465    {
466       cf.rollback(tx);
467    }
468
469    // Service implementation
470

471    public void create() throws Exception JavaDoc
472    {
473    }
474
475    public void start() throws Exception JavaDoc
476    {
477       if (config.getDriverClass() != null)
478       {
479          loadDriver(config.getDriverClass());
480       }
481       else
482       {
483          // A datasource will be registered in JNDI in the start portion of
484
// its lifecycle, so now that we are in start() we can look it up
485
InitialContext JavaDoc ctx = null;
486          try
487          {
488             ctx = new InitialContext JavaDoc();
489             DataSource JavaDoc dataSource = (DataSource JavaDoc) ctx.lookup(config.getDatasourceName());
490             this.cf = new ManagedConnectionFactory(dataSource);
491          }
492          catch (NamingException JavaDoc e)
493          {
494             log.error("Failed to lookup datasource " + config.getDatasourceName() + ": " + e.getMessage(), e);
495             throw new IllegalStateException JavaDoc("Failed to lookup datasource " + config.getDatasourceName() + ": " + e.getMessage());
496          }
497          finally
498          {
499             if (ctx != null)
500             {
501                try
502                {
503                   ctx.close();
504                }
505                catch (NamingException JavaDoc e)
506                {
507                   log.warn("Failed to close naming context.", e);
508                }
509             }
510          }
511       }
512
513       Connection JavaDoc con = null;
514       Statement JavaDoc st = null;
515
516       try
517       {
518          con = cf.getConnection();
519          driverName = getDriverName(con);
520          if (config.getCreateTable())
521          {
522             if (!tableExists(config.getTable(), con))
523             {
524                if (log.isDebugEnabled())
525                {
526                   log.debug("executing ddl: " + config.getCreateTableDDL());
527                }
528                st = con.createStatement();
529                st.executeUpdate(config.getCreateTableDDL());
530             }
531          }
532       }
533       finally
534       {
535          safeClose(st);
536          cf.close(con);
537       }
538    }
539
540    public void stop()
541    {
542       if (config.getDropTable())
543       {
544          Connection JavaDoc con = null;
545          Statement JavaDoc st = null;
546          try
547          {
548             if (log.isDebugEnabled())
549             {
550                log.debug("executing ddl: " + config.getDropTableDDL());
551             }
552
553             con = cf.getConnection();
554             st = con.createStatement();
555             st.executeUpdate(config.getDropTableDDL());
556             safeClose(st);
557          }
558          catch (SQLException JavaDoc e)
559          {
560             log.error("Failed to drop table: " + e.getMessage(), e);
561          }
562          finally
563          {
564             safeClose(st);
565             cf.close(con);
566          }
567       }
568    }
569
570    public void destroy()
571    {
572    }
573
574    // Private
575

576    private void addChildrenToDeleteSql(String JavaDoc name, StringBuffer JavaDoc sql, List JavaDoc fqns)
577            throws SQLException JavaDoc
578    {
579       // for now have to use connection per method, i.e. can't pass the same connection to recursive
580
// invocations because buggy PointBase driver invalidates result sets.
581
Connection JavaDoc con = null;
582       PreparedStatement JavaDoc selChildrenPs = null;
583       ResultSet JavaDoc rs = null;
584       try
585       {
586          if (log.isDebugEnabled())
587          {
588             log.debug("executing sql: " + config.getSelectChildFqnsSql() + "(" + name + ")");
589          }
590
591          con = cf.getConnection();
592          selChildrenPs = con.prepareStatement(config.getSelectChildFqnsSql());
593          selChildrenPs.setString(1, name);
594          rs = selChildrenPs.executeQuery();
595
596          if (rs.next())
597          {
598             do
599             {
600                String JavaDoc childStr = rs.getString(1);
601                addChildrenToDeleteSql(childStr, sql, fqns);
602             }
603             while (rs.next());
604          }
605
606          if (fqns.size() == 0)
607          {
608             sql.append("?");
609          }
610          else
611          {
612             sql.append(", ?");
613          }
614          fqns.add(name);
615       }
616       finally
617       {
618          safeClose(rs);
619          safeClose(selChildrenPs);
620          cf.close(con);
621       }
622    }
623
624    public void put(Fqn name, Map JavaDoc attributes, boolean override) throws Exception JavaDoc
625    {
626       // JBCACHE-769 -- make a defensive copy
627
Map JavaDoc attrs = (attributes == null ? null : new HashMap JavaDoc(attributes));
628
629       Map JavaDoc oldNode = loadNode(name);
630       if (oldNode != null)
631       {
632          if (!override && oldNode != NULL_NODE_IN_ROW && attrs != null)
633          {
634             attrs.putAll(oldNode);
635          }
636          updateNode(name, attrs);
637       }
638       else
639       {
640          if (name.size() > 1)
641          {
642             for (int i = 1; i < name.size(); ++i)
643             {
644                final Fqn parent = name.getFqnChild(i);
645                if (!exists(parent))
646                {
647                   insertNode(parent, null);
648                }
649             }
650          }
651          insertNode(name, attrs);
652       }
653    }
654
655    /**
656     * Inserts a node into the database
657     *
658     * @param name the fqn
659     * @param node the node
660     */

661    private void insertNode(Fqn name, Map JavaDoc node)
662    {
663       Connection JavaDoc con = null;
664       PreparedStatement JavaDoc ps = null;
665       try
666       {
667          if (log.isDebugEnabled())
668          {
669             log.debug("executing sql: " + config.getInsertNodeSql() + " (" + name + ")");
670          }
671
672          con = cf.getConnection();
673          ps = con.prepareStatement(config.getInsertNodeSql());
674
675          ps.setString(1, name.toString());
676
677          if (node != null)
678          {
679 // Object marshalledNode = new MarshalledValue(node);
680
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
681             ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(baos);
682 // oos.writeObject(marshalledNode);
683
//
684
oos.writeObject(node);
685
686             ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(baos.toByteArray());
687             ps.setBinaryStream(2, bais, baos.size());
688          }
689          else
690          {
691             // a hack to handles the incomp. of SQL server jdbc driver prior to SQL SERVER 2005
692
if (driverName != null && (driverName.contains("SQLSERVER")
693                     || driverName.contains("POSTGRESQL")))
694             {
695                ps.setNull(2, Types.LONGVARBINARY);
696             }
697             else
698             {
699                ps.setNull(2, Types.BLOB);
700             }
701             //ps.setNull(2, Types.LONGVARBINARY);
702
}
703
704          if (name.size() == 0)
705          {
706             ps.setNull(3, Types.VARCHAR);
707          }
708          else
709          {
710             ps.setString(3, name.getFqnChild(name.size() - 1).toString());
711          }
712
713          int rows = ps.executeUpdate();
714          if (rows != 1)
715          {
716             throw new IllegalStateException JavaDoc("Expected one insert row but got " + rows);
717          }
718       }
719       catch (RuntimeException JavaDoc e)
720       {
721          throw e;
722       }
723       catch (Exception JavaDoc e)
724       {
725          log.error("Failed to insert node: " + e.getMessage(), e);
726          throw new IllegalStateException JavaDoc("Failed to insert node: " + e.getMessage());
727       }
728       finally
729       {
730          safeClose(ps);
731          cf.close(con);
732       }
733    }
734
735    /**
736     * Updates a node in the database.
737     *
738     * @param name the fqn
739     * @param node new node value
740     */

741    private void updateNode(Fqn name, Map JavaDoc node)
742    {
743       Connection JavaDoc con = null;
744       PreparedStatement JavaDoc ps = null;
745       try
746       {
747          if (log.isDebugEnabled())
748          {
749             log.debug("executing sql: " + config.getUpdateNodeSql());
750          }
751
752          con = cf.getConnection();
753          ps = con.prepareStatement(config.getUpdateNodeSql());
754
755          if (node == null)
756          {
757             //ps.setNull(1, Types.BLOB);
758
// ps.setNull(1, Types.LONGVARBINARY);
759
// don't set it to null - simply use an empty hash map.
760
node = new HashMap JavaDoc(0);
761          }
762          // else
763
// {
764
// Object marshalledNode = new MarshalledValue(node);
765
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
766          ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(baos);
767 // oos.writeObject(marshalledNode);
768
oos.writeObject(node);
769
770          ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(baos.toByteArray());
771          ps.setBinaryStream(1, bais, baos.size());
772          // }
773

774          ps.setString(2, name.toString());
775
776          int rows = ps.executeUpdate();
777 // if (rows != 1)
778
// {
779
// throw new IllegalStateException("Expected one updated row but got " + rows);
780
// }
781
}
782       catch (Exception JavaDoc e)
783       {
784          log.error("Failed to update node for fqn " + name + ": " + e.getMessage(), e);
785          throw new IllegalStateException JavaDoc("Failed to update node for fqn " + name + ": " + e.getMessage());
786       }
787       finally
788       {
789          safeClose(ps);
790          cf.close(con);
791       }
792    }
793
794    /**
795     * Loads a node from the database.
796     *
797     * @param name the fqn
798     * @return non-null Map representing the node,
799     * null if there is no row with the fqn in the table,
800     * NULL_NODE_IN_ROW if there is a row in the table with the fqn but the node column contains null.
801     */

802    private Map JavaDoc loadNode(Fqn name)
803    {
804       boolean rowExists = false;
805       Map JavaDoc oldNode = null;
806       Connection JavaDoc con = null;
807       PreparedStatement JavaDoc ps = null;
808       ResultSet JavaDoc rs = null;
809       try
810       {
811          if (log.isDebugEnabled())
812          {
813             log.debug("executing sql: " + config.getSelectNodeSql() + " (" + name + ")");
814          }
815
816          con = cf.getConnection();
817          ps = con.prepareStatement(config.getSelectNodeSql());
818          ps.setString(1, name.toString());
819
820          rs = ps.executeQuery();
821
822          if (rs.next())
823          {
824             rowExists = true;
825             InputStream JavaDoc is = rs.getBinaryStream(1);
826             if (is != null && !rs.wasNull())
827             {
828                ObjectInputStream JavaDoc ois = null;
829                try
830                {
831                   // deserialize result
832
ois = new ObjectInputStream JavaDoc(is);
833                   Object JavaDoc marshalledNode = ois.readObject();
834
835                   // de-marshall value if possible
836
// if (marshalledNode instanceof MarshalledValue)
837
// {
838
// oldNode = (Map) ((MarshalledValue) marshalledNode).get();
839
// }
840
// else if (marshalledNode instanceof MarshalledObject)
841
// {
842
// oldNode = (Map) ((MarshalledObject) marshalledNode).get();
843
// }
844
oldNode = (Map JavaDoc) marshalledNode;
845                }
846                catch (IOException JavaDoc e)
847                {
848                   throw new SQLException JavaDoc("Unable to load to deserialize result: " + e);
849                }
850                catch (ClassNotFoundException JavaDoc e)
851                {
852                   throw new SQLException JavaDoc("Unable to load to deserialize result: " + e);
853                }
854                finally
855                {
856                   safeClose(ois);
857                }
858             }
859          }
860       }
861       catch (SQLException JavaDoc e)
862       {
863          log.error("Failed to load node for fqn " + name + ": " + e.getMessage(), e);
864          throw new IllegalStateException JavaDoc("Failed to load node for fqn " + name + ": " + e.getMessage());
865       }
866       finally
867       {
868          safeClose(rs);
869          safeClose(ps);
870          cf.close(con);
871       }
872
873       return oldNode == null ? (rowExists ? NULL_NODE_IN_ROW : null) : oldNode;
874    }
875
876    private static void safeClose(InputStream JavaDoc is)
877    {
878       if (is != null)
879       {
880          try
881          {
882             is.close();
883          }
884          catch (IOException JavaDoc e)
885          {
886             log.warn("Failed to close input stream: " + e.getMessage());
887          }
888       }
889    }
890
891    private static void safeClose(Connection JavaDoc con)
892    {
893       if (con != null)
894       {
895          try
896          {
897             con.close();
898          }
899          catch (SQLException JavaDoc e)
900          {
901             log.warn("Failed to close connection: " + e.getMessage());
902          }
903       }
904    }
905
906    private static void safeClose(Statement JavaDoc st)
907    {
908       if (st != null)
909       {
910          try
911          {
912             st.close();
913          }
914          catch (SQLException JavaDoc e)
915          {
916             log.warn("Failed to close statement: " + e.getMessage());
917          }
918       }
919    }
920
921    private static void safeClose(ResultSet JavaDoc rs)
922    {
923       if (rs != null)
924       {
925          try
926          {
927             rs.close();
928          }
929          catch (SQLException JavaDoc e)
930          {
931             log.warn("Failed to close result set: " + e.getMessage());
932          }
933       }
934    }
935
936    private static void loadDriver(String JavaDoc drv)
937    {
938       try
939       {
940          Class.forName(drv).newInstance();
941       }
942       catch (Exception JavaDoc e)
943       {
944          log.error("Failed to load driver " + drv, e);
945          throw new IllegalStateException JavaDoc("Failed to load driver " + drv + ": " + e.getMessage());
946       }
947    }
948
949    private static String JavaDoc getDriverName(Connection JavaDoc con)
950    {
951       if (con == null) return null;
952       try
953       {
954          DatabaseMetaData JavaDoc dmd = con.getMetaData();
955          return dmd.getDriverName().toUpperCase();
956       }
957       catch (SQLException JavaDoc e)
958       {
959          // This should not happen. A J2EE compatiable JDBC driver is
960
// required to fully support metadata.
961
throw new IllegalStateException JavaDoc(
962                  "Error while getting the driver name " + ": " + e.getMessage());
963       }
964    }
965
966    static String JavaDoc getRequiredProperty(Properties JavaDoc props, String JavaDoc name)
967    {
968       String JavaDoc value = props.getProperty(name);
969       if (value == null)
970       {
971          throw new IllegalStateException JavaDoc("Missing required property: " + name);
972       }
973       return value;
974    }
975
976    private static boolean tableExists(String JavaDoc tableName, Connection JavaDoc con)
977    {
978       ResultSet JavaDoc rs = null;
979       try
980       {
981          // (a j2ee spec compatible jdbc driver has to fully
982
// implement the DatabaseMetaData)
983
DatabaseMetaData JavaDoc dmd = con.getMetaData();
984          String JavaDoc catalog = con.getCatalog();
985          String JavaDoc schema = null;
986          String JavaDoc quote = dmd.getIdentifierQuoteString();
987          if (tableName.startsWith(quote))
988          {
989             if (!tableName.endsWith(quote))
990             {
991                throw new IllegalStateException JavaDoc("Mismatched quote in table name: " + tableName);
992             }
993             int quoteLength = quote.length();
994             tableName = tableName.substring(quoteLength, tableName.length() - quoteLength);
995             if (dmd.storesLowerCaseQuotedIdentifiers())
996             {
997                tableName = tableName.toLowerCase();
998             }
999             else if (dmd.storesUpperCaseQuotedIdentifiers())
1000            {
1001               tableName = tableName.toUpperCase();
1002            }
1003         }
1004         else
1005         {
1006            if (dmd.storesLowerCaseIdentifiers())
1007            {
1008               tableName = tableName.toLowerCase();
1009            }
1010            else if (dmd.storesUpperCaseIdentifiers())
1011            {
1012               tableName = tableName.toUpperCase();
1013            }
1014         }
1015
1016         int dotIndex;
1017         if ((dotIndex = tableName.indexOf('.')) != -1)
1018         {
1019            // Yank out schema name ...
1020
schema = tableName.substring(0, dotIndex);
1021            tableName = tableName.substring(dotIndex + 1);
1022         }
1023
1024         rs = dmd.getTables(catalog, schema, tableName, null);
1025         return rs.next();
1026      }
1027      catch (SQLException JavaDoc e)
1028      {
1029         // This should not happen. A J2EE compatiable JDBC driver is
1030
// required fully support metadata.
1031
throw new IllegalStateException JavaDoc(
1032                 "Error while checking if table aleady exists " + tableName + ": " + e.getMessage());
1033      }
1034      finally
1035      {
1036         safeClose(rs);
1037      }
1038   }
1039
1040   // Inner
1041

1042   private static final Map JavaDoc NULL_NODE_IN_ROW = new Map JavaDoc()
1043   {
1044      public int size()
1045      {
1046         throw new UnsupportedOperationException JavaDoc();
1047      }
1048
1049      public void clear()
1050      {
1051         throw new UnsupportedOperationException JavaDoc();
1052      }
1053
1054      public boolean isEmpty()
1055      {
1056         throw new UnsupportedOperationException JavaDoc();
1057      }
1058
1059      public boolean containsKey(Object JavaDoc key)
1060      {
1061         throw new UnsupportedOperationException JavaDoc();
1062      }
1063
1064      public boolean containsValue(Object JavaDoc value)
1065      {
1066         throw new UnsupportedOperationException JavaDoc();
1067      }
1068
1069      public Collection JavaDoc values()
1070      {
1071         throw new UnsupportedOperationException JavaDoc();
1072      }
1073
1074      public void putAll(Map JavaDoc t)
1075      {
1076         throw new UnsupportedOperationException JavaDoc();
1077      }
1078
1079      public Set JavaDoc entrySet()
1080      {
1081         throw new UnsupportedOperationException JavaDoc();
1082      }
1083
1084      public Set JavaDoc keySet()
1085      {
1086         throw new UnsupportedOperationException JavaDoc();
1087      }
1088
1089      public Object JavaDoc get(Object JavaDoc key)
1090      {
1091         throw new UnsupportedOperationException JavaDoc();
1092      }
1093
1094      public Object JavaDoc remove(Object JavaDoc key)
1095      {
1096         throw new UnsupportedOperationException JavaDoc();
1097      }
1098
1099      public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value)
1100      {
1101         throw new UnsupportedOperationException JavaDoc();
1102      }
1103   };
1104
1105   interface ConnectionFactory
1106   {
1107      Connection JavaDoc getConnection() throws SQLException JavaDoc;
1108
1109      Connection JavaDoc prepare(Object JavaDoc tx);
1110
1111      void commit(Object JavaDoc tx);
1112
1113      void rollback(Object JavaDoc tx);
1114
1115      void close(Connection JavaDoc con);
1116   }
1117
1118   private final class NonManagedConnectionFactory implements ConnectionFactory
1119   {
1120      private final String JavaDoc url;
1121      private final String JavaDoc usr;
1122      private final String JavaDoc pwd;
1123
1124      public NonManagedConnectionFactory(String JavaDoc url, String JavaDoc usr, String JavaDoc pwd)
1125      {
1126         this.url = url;
1127         this.usr = usr;
1128         this.pwd = pwd;
1129      }
1130
1131      public Connection JavaDoc prepare(Object JavaDoc tx)
1132      {
1133         Connection JavaDoc con = getConnection();
1134         try
1135         {
1136            if (con.getAutoCommit())
1137            {
1138               con.setAutoCommit(false);
1139            }
1140         }
1141         catch (Exception JavaDoc e)
1142         {
1143            log.error("Failed to set auto-commit: " + e.getMessage(), e);
1144            throw new IllegalStateException JavaDoc("Failed to set auto-commit: " + e.getMessage());
1145         }
1146         connection.set(con);
1147         return con;
1148      }
1149
1150      public Connection JavaDoc getConnection()
1151      {
1152         Connection JavaDoc con = (Connection JavaDoc) connection.get();
1153         if (con == null)
1154         {
1155            try
1156            {
1157               con = DriverManager.getConnection(url, usr, pwd);
1158// connection.set(con);
1159
}
1160            catch (SQLException JavaDoc e)
1161            {
1162               log.error("Failed to get connection for url=" + url + ", user=" + usr + ", password=" + pwd, e);
1163               throw new IllegalStateException JavaDoc("Failed to get connection for url=" +
1164                       url +
1165                       ", user=" +
1166                       usr +
1167                       ", password=" +
1168                       pwd +
1169                       ": " +
1170                       e.getMessage());
1171            }
1172         }
1173
1174         if (log.isTraceEnabled())
1175         {
1176            log.debug("using connection: " + con);
1177         }
1178
1179         return con;
1180      }
1181
1182      public void commit(Object JavaDoc tx)
1183      {
1184         Connection JavaDoc con = (Connection JavaDoc) connection.get();
1185         if (con == null)
1186         {
1187            throw new IllegalStateException JavaDoc("Failed to commit: thread is not associated with the connection!");
1188         }
1189
1190         try
1191         {
1192            con.commit();
1193            if (log.isTraceEnabled())
1194            {
1195               log.trace("committed tx=" + tx + ", con=" + con);
1196            }
1197         }
1198         catch (SQLException JavaDoc e)
1199         {
1200            log.error("Failed to commit", e);
1201            throw new IllegalStateException JavaDoc("Failed to commit: " + e.getMessage());
1202         }
1203         finally
1204         {
1205            closeTxConnection(con);
1206         }
1207      }
1208
1209      public void rollback(Object JavaDoc tx)
1210      {
1211         Connection JavaDoc con = (Connection JavaDoc) connection.get();
1212         if (con == null)
1213         {
1214            // todo: prepare was not called. why is rollback called?
1215
throw new IllegalStateException JavaDoc("Failed to rollback: thread is not associated with the connection!");
1216         }
1217
1218         try
1219         {
1220            con.rollback();
1221            if (log.isTraceEnabled())
1222            {
1223               log.trace("rolledback tx=" + tx + ", con=" + con);
1224            }
1225         }
1226         catch (SQLException JavaDoc e)
1227         {
1228            log.error("Failed to rollback", e);
1229            throw new IllegalStateException JavaDoc("Failed to rollback: " + e.getMessage());
1230         }
1231         finally
1232         {
1233            closeTxConnection(con);
1234         }
1235      }
1236
1237      public void close(Connection JavaDoc con)
1238      {
1239         if (con != null && con != connection.get())
1240         {
1241            try
1242            {
1243               con.close();
1244               if (log.isTraceEnabled())
1245               {
1246                  //log.trace("closed non tx connection: " + con);
1247
}
1248            }
1249            catch (SQLException JavaDoc e)
1250            {
1251               log.warn("Failed to close connection: " + e.getMessage());
1252            }
1253         }
1254      }
1255
1256      private void closeTxConnection(Connection JavaDoc con)
1257      {
1258         safeClose(con);
1259         connection.set(null);
1260      }
1261   }
1262
1263   private final class ManagedConnectionFactory
1264           implements ConnectionFactory
1265   {
1266      private final DataSource JavaDoc dataSource;
1267
1268      public ManagedConnectionFactory(DataSource JavaDoc dataSource)
1269      {
1270         // Test that ds isn't null. This wouldn't happen in the real
1271
// world as the JNDI lookup would fail, but this check here
1272
// allows a unit test using DummyContext
1273
if (dataSource == null)
1274         {
1275            throw new IllegalArgumentException JavaDoc("dataSource cannot be null");
1276         }
1277
1278         this.dataSource = dataSource;
1279      }
1280
1281      public Connection JavaDoc prepare(Object JavaDoc tx)
1282      {
1283         // we don't need to set autocommit to false beause the DataSource object
1284
// is almost always has a connection pool associated with it and distributed
1285
// transaction participation turned on which means autocommit is off by default
1286

1287         try
1288         {
1289            return getConnection();
1290         }
1291         catch (SQLException JavaDoc e)
1292         {
1293            log.error("Failed to get connection: " + e.getMessage(), e);
1294            throw new IllegalStateException JavaDoc("Failed to get connection: " + e.getMessage());
1295         }
1296      }
1297
1298      public Connection JavaDoc getConnection()
1299              throws SQLException JavaDoc
1300      {
1301         return dataSource.getConnection();
1302      }
1303
1304      public void commit(Object JavaDoc tx)
1305      {
1306      }
1307
1308      public void rollback(Object JavaDoc tx)
1309      {
1310      }
1311
1312      public void close(Connection JavaDoc con)
1313      {
1314         safeClose(con);
1315      }
1316   }
1317}
1318
Popular Tags