KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > db > JdbcConnectionResource


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.quercus.lib.db;
31
32 import com.caucho.quercus.QuercusModuleException;
33 import com.caucho.quercus.annotation.Optional;
34 import com.caucho.quercus.env.BooleanValue;
35 import com.caucho.quercus.env.Env;
36 import com.caucho.quercus.env.StringBuilderValue;
37 import com.caucho.quercus.env.StringValue;
38 import com.caucho.quercus.env.StringValueImpl;
39 import com.caucho.quercus.env.Value;
40 import com.caucho.util.L10N;
41 import com.caucho.util.LruCache;
42
43 import java.io.Closeable JavaDoc;
44 import java.sql.*;
45 import java.util.logging.Level JavaDoc;
46 import java.util.logging.Logger JavaDoc;
47
48 /**
49  * Represents a JDBC Connection value.
50  */

51 public abstract class JdbcConnectionResource implements Closeable JavaDoc {
52   private static final L10N L = new L10N(JdbcConnectionResource.class);
53   private static final Logger JavaDoc log
54     = Logger.getLogger(JdbcConnectionResource.class.getName());
55
56   private static LruCache<TableKey,JdbcTableMetaData> _tableMetadataMap
57     = new LruCache<TableKey,JdbcTableMetaData>(256);
58
59   private Connection _conn;
60   // cached statement
61
private Statement _stmt;
62
63   private DatabaseMetaData _dmd;
64
65   private JdbcResultResource _rs;
66   private int _affectedRows;
67
68   private String JavaDoc _errorMessage = "";
69   private int _errorCode;
70   private boolean _fieldCount = false;
71   private SQLWarning _warnings;
72
73   private Env _env;
74   private String JavaDoc _host;
75   private String JavaDoc _dbname;
76   private int _port;
77   private String JavaDoc _userName;
78   private String JavaDoc _password;
79   private String JavaDoc _driver;
80   private String JavaDoc _url;
81
82   private boolean _connected;
83
84   public JdbcConnectionResource(Env env)
85   {
86     _env = env;
87   }
88
89   /**
90    * Returns the error string for the most recent function call
91    */

92   public String JavaDoc error()
93   {
94     if (isConnected())
95       return getErrorMessage();
96     else
97       return null;
98   }
99
100   public boolean isConnected()
101   {
102     return _connected;
103   }
104
105   public Env getEnv()
106   {
107     return _env;
108   }
109
110   public String JavaDoc getHost()
111   {
112     return _host;
113   }
114
115   public String JavaDoc getUserName()
116   {
117     return _userName;
118   }
119
120   public String JavaDoc getPassword()
121   {
122     return _password;
123   }
124
125   public String JavaDoc getDbName()
126   {
127     return _dbname;
128   }
129
130   public int getPort()
131   {
132     return _port;
133   }
134
135   public String JavaDoc getDriver()
136   {
137     return _driver;
138   }
139
140   public String JavaDoc getUrl()
141   {
142     return _url;
143   }
144
145   /**
146    * Set the current underlying connection and
147    * corresponding information: host, port and
148    * database name.
149    *
150    * @param host server host
151    * @param port server port
152    * @param dbname database name
153    */

154   protected void setConnection(String JavaDoc host,
155                                String JavaDoc userName,
156                                String JavaDoc password,
157                                String JavaDoc dbname,
158                                int port,
159                                Connection conn,
160                                String JavaDoc driver,
161                                String JavaDoc url)
162   {
163     _host = host;
164     _userName = userName;
165     _password = password;
166     _dbname = dbname;
167     _port = port;
168
169     _conn = conn;
170
171     _driver = driver;
172
173     _url = url;
174
175     if (conn != null) {
176       _connected = true;
177
178       _env.addClose(this);
179     }
180   }
181
182   /**
183    * Connects to the underlying database.
184    */

185   protected abstract boolean connectInternal(Env env,
186                                              @Optional("localhost") String JavaDoc host,
187                                              @Optional String JavaDoc userName,
188                                              @Optional String JavaDoc password,
189                                              @Optional String JavaDoc dbname,
190                                              @Optional int port,
191                                              @Optional String JavaDoc socket,
192                                              @Optional int flags,
193                                              @Optional String JavaDoc driver,
194                                              @Optional String JavaDoc url);
195
196   /**
197    * Escape the given string for SQL statements.
198    *
199    * @param str a string
200    * @return the string escaped for SQL statements
201    */

202   protected StringBuilderValue realEscapeString(StringValue str)
203   {
204     StringBuilderValue buf = new StringBuilderValue(str.length());
205
206     final int strLength = str.length();
207
208     for (int i = 0; i < strLength; i++) {
209       char c = str.charAt(i);
210
211       switch (c) {
212       case '\u0000':
213         buf.append('\\');
214         buf.append('\u0000');
215         break;
216       case '\n':
217         buf.append('\\');
218         buf.append('n');
219         break;
220       case '\r':
221         buf.append('\\');
222         buf.append('r');
223         break;
224       case '\\':
225         buf.append('\\');
226         buf.append('\\');
227         break;
228       case '\'':
229         buf.append('\\');
230         buf.append('\'');
231         break;
232       case '"':
233         buf.append('\\');
234         buf.append('\"');
235         break;
236       case '\032':
237         buf.append('\\');
238         buf.append('Z');
239         break;
240       default:
241         buf.append(c);
242         break;
243       }
244     }
245
246     return buf;
247   }
248
249   /**
250    * Returns the affected rows from the last query.
251    */

252   public int getAffectedRows()
253   {
254     return _affectedRows;
255   }
256
257   public void setAffectedRows(int i)
258   {
259     _affectedRows = i;
260   }
261
262   /**
263    * @return _fieldCount
264    */

265   public int getFieldCount()
266   {
267     if (_rs == null) {
268       return 0;
269     } else {
270       return _rs.getFieldCount();
271     }
272   }
273
274   /**
275    * Returns JdbcResultResource of available databases
276    */

277   protected JdbcResultResource getCatalogs()
278   {
279     clearErrors();
280
281     try {
282       if (_dmd == null)
283         _dmd = _conn.getMetaData();
284
285       ResultSet rs = _dmd.getCatalogs();
286
287       if (rs != null)
288         return createResult(_stmt, rs);
289       else
290         return null;
291     } catch (SQLException e) {
292       saveErrors(e);
293       log.log(Level.WARNING, e.toString(), e);
294       return null;
295     }
296   }
297
298   /**
299    * @return current catalog or false if error
300    */

301   protected Value getCatalog()
302   {
303     clearErrors();
304
305     try {
306       return new StringValueImpl(_conn.getCatalog());
307     } catch (SQLException e) {
308       saveErrors(e);
309       log.log(Level.WARNING, e.toString(), e);
310       return BooleanValue.FALSE;
311     }
312   }
313
314   /**
315    * Returns the client encoding.
316    *
317    * XXX: stubbed out. has to be revised once we
318    * figure out what to do with character encoding
319    */

320   public String JavaDoc getCharacterSetName()
321   {
322     return "latin1";
323   }
324
325   /**
326    * Alias for getCharacterSetName
327    */

328   public String JavaDoc getClientEncoding()
329   {
330     return getCharacterSetName();
331   }
332
333   /**
334    * Returns the client version
335    * @deprecated
336    */

337   public String JavaDoc getClientInfo()
338   {
339     try {
340       if (_dmd == null)
341         _dmd = _conn.getMetaData();
342
343       return _dmd.getDatabaseProductVersion();
344     } catch (SQLException e) {
345       log.log(Level.FINE, e.toString(), e);
346       return null;
347     }
348   }
349
350   /**
351    * Returns the connection
352    */

353   public Connection getConnection()
354   {
355     if (_conn != null)
356       return _conn;
357     else if (_errorMessage != null)
358       throw new QuercusModuleException(_errorMessage);
359     else
360       throw new QuercusModuleException(L.l("Connection is not available"));
361   }
362
363   /**
364    * Returns the underlying SQL connection
365    * associated to this statement.
366    */

367   protected Connection getJavaConnection()
368     throws SQLException
369   {
370     return _env.getQuercus().getConnection(_conn);
371   }
372
373   /**
374    * Returns the data source.
375    */

376   public String JavaDoc getURL()
377   {
378     // return getJavaConnection().getURL();
379
return _url;
380   }
381
382   /**
383    * Returns the last error code.
384    */

385   public int getErrorCode()
386   {
387     return _errorCode;
388   }
389
390   /**
391    * Returns the last error message.
392    */

393   public String JavaDoc getErrorMessage()
394   {
395     return _errorMessage;
396   }
397
398   /**
399    *
400    * returns the URL string for the given connection
401    * IE: jdbc:mysql://localhost:3306/test
402    * XXX: PHP returns Localhost via UNIX socket
403    */

404   public String JavaDoc getHostInfo()
405     throws SQLException
406   {
407     if (_dmd == null)
408       _dmd = _conn.getMetaData();
409
410     return _dmd.getURL();
411   }
412
413   /**
414    * returns the server version
415    * XXX: PHP seems to return the same value
416    * for client_info and server_info
417    */

418   public String JavaDoc getServerInfo()
419     throws SQLException
420   {
421     return getMetaData().getDatabaseProductVersion();
422   }
423
424   /**
425    * Returns the table metadata.
426    */

427   public JdbcTableMetaData getTableMetaData(String JavaDoc catalog,
428                                             String JavaDoc schema,
429                                             String JavaDoc table)
430     throws SQLException
431   {
432     TableKey key = new TableKey(getURL(), catalog, schema, table);
433
434     // XXX: needs invalidation on DROP or ALTER
435
JdbcTableMetaData tableMd = _tableMetadataMap.get(key);
436
437     if (tableMd != null && tableMd.isValid())
438       return tableMd;
439
440     tableMd = new JdbcTableMetaData(catalog, schema, table, getMetaData());
441
442     _tableMetadataMap.put(key, tableMd);
443
444     return tableMd;
445   }
446
447   private DatabaseMetaData getMetaData()
448     throws SQLException
449   {
450     if (_dmd == null)
451       _dmd = _conn.getMetaData();
452
453     return _dmd;
454   }
455
456   static int infoToVersion(String JavaDoc info)
457   {
458     String JavaDoc[] result = info.split("[.a-z-]");
459
460     if (result.length < 3)
461       return 0;
462
463     return (Integer.parseInt(result[0]) * 10000 +
464             Integer.parseInt(result[1]) * 100 +
465             Integer.parseInt(result[2]));
466   }
467
468   /**
469    * Closes the connection.
470    */

471   public boolean close(Env env)
472   {
473     if (_connected) {
474       _connected = false;
475       env.removeClose(this);
476       close();
477     }
478
479     return true;
480   }
481
482   public JdbcConnectionResource validateConnection()
483   {
484     if (! _connected) {
485       throw _env.errorException(L.l("Connection is not properly initialized {0}\nDriver {1}",
486                                     _url, _driver));
487     }
488
489     return this;
490   }
491
492   /**
493    * Execute a single query.
494    */

495   protected JdbcResultResource realQuery(String JavaDoc sql)
496   {
497     clearErrors();
498
499     _rs = null;
500
501     Statement stmt = null;
502
503     try {
504       stmt = getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
505                                              ResultSet.CONCUR_READ_ONLY);
506       stmt.setEscapeProcessing(false); // php/1406
507

508       if (stmt.execute(sql)) {
509         ResultSet rs = stmt.getResultSet();
510         _rs = createResult(stmt, rs);
511         _affectedRows = 0;
512         _warnings = stmt.getWarnings();
513       } else {
514         // php/430a should return a result set
515
// for update statements. It is always
516
// null though. So keep the stmt for
517
// future reference (PostgresModule.pg_last_oid)
518

519         // php/1f33
520

521         // This is overriden in Postgres.java
522
keepResourceValues(stmt);
523
524         _affectedRows = 0;
525         _affectedRows = stmt.getUpdateCount();
526         if (_rs != null)
527           _rs.setAffectedRows(_affectedRows);
528         _warnings = stmt.getWarnings();
529
530         // for php/430a
531
if (!keepStatementOpen()) {
532           stmt.close();
533         }
534       }
535     } catch (DataTruncation truncationError) {
536       try {
537         _affectedRows = stmt.getUpdateCount();
538         _warnings = stmt.getWarnings();
539       } catch (SQLException e) {
540         saveErrors(e);
541         log.log(Level.WARNING, e.toString(), e);
542         return null;
543       }
544     } catch (SQLException e) {
545
546       saveErrors(e);
547
548       // php/431h
549
if (keepStatementOpen()) {
550         keepResourceValues(stmt);
551       } else {
552         log.log(Level.WARNING, e.toString(), e);
553         return null;
554       }
555     }
556
557     return _rs;
558   }
559
560   /**
561    * Creates a database-specific result.
562    */

563   protected JdbcResultResource createResult(Statement stmt,
564                                             ResultSet rs)
565   {
566     return new JdbcResultResource(stmt, rs, this);
567   }
568
569
570   /**
571    * sets auto-commmit to true or false
572    */

573   public boolean setAutoCommit(boolean mode)
574   {
575     clearErrors();
576
577     try {
578       _conn.setAutoCommit(mode);
579     } catch (SQLException e) {
580       saveErrors(e);
581       log.log(Level.WARNING, e.toString(), e);
582       return false;
583     }
584
585     return true;
586   }
587
588   /**
589    * commits the transaction of the current connection
590    */

591   public boolean commit()
592   {
593     clearErrors();
594
595     try {
596       _conn.commit();
597     } catch (SQLException e) {
598       saveErrors(e);
599       log.log(Level.WARNING, e.toString(), e);
600       return false;
601     }
602
603     return true;
604   }
605
606   /**
607    * rolls the current transaction back
608    *
609    * NOTE: quercus doesn't seem to support the idea
610    * of savepoints
611    */

612   public boolean rollback()
613   {
614     clearErrors();
615
616     try {
617       _conn.rollback();
618     } catch (SQLException e) {
619       saveErrors(e);
620       log.log(Level.WARNING, e.toString(), e);
621       return false;
622     }
623
624     return true;
625   }
626   /**
627    * Sets the catalog
628    */

629   public void setCatalog(String JavaDoc name)
630     throws SQLException
631   {
632     clearErrors();
633
634     _conn.setCatalog(name);
635   }
636
637   /**
638    * Converts to an object.
639    */

640   public Object JavaDoc toObject()
641   {
642     return null;
643   }
644
645   /**
646    * Converts to a string.
647    */

648   public String JavaDoc toString()
649   {
650     return _conn.toString();
651   }
652
653   /**
654    * Closes the connection.
655    */

656   public void close()
657   {
658     try {
659       Statement stmt = _stmt;
660       _stmt = null;
661
662       if (stmt != null)
663         stmt.close();
664     } catch (SQLException e) {
665       log.log(Level.FINER, e.toString(), e);
666     }
667
668     try {
669       Connection conn = _conn;
670       // XXX: since the above code doesn't check for _conn == null can't null
671
// _conn = null;
672

673       if (conn != null)
674         conn.close();
675     } catch (SQLException e) {
676       log.log(Level.FINER, e.toString(), e);
677     }
678   }
679
680   /**
681    * This function is overriden in Postgres to keep
682    * result set references for php/430a (see also php/1f33)
683    */

684   protected void keepResourceValues(Statement stmt)
685   {
686     return;
687   }
688
689   /**
690    * This function is overriden in Postgres to keep
691    * statement references for php/430a
692    */

693   protected boolean keepStatementOpen()
694   {
695     return false;
696   }
697
698   /**
699    * Get the current result resource
700    */

701   protected JdbcResultResource getResultResource()
702   {
703     return _rs;
704   }
705
706   /**
707    * Set the current result resource
708    */

709   protected void setResultResource(JdbcResultResource rs)
710   {
711     _rs = rs;
712   }
713
714   /**
715    * This function was added for PostgreSQL pg_last_notice
716    *
717    * @return warning messages
718    */

719   protected SQLWarning getWarnings()
720   {
721     return _warnings;
722   }
723
724   /**
725    * Pings the database
726    */

727   public boolean ping()
728   {
729     try {
730
731       return isConnected() && ! getConnection().isClosed();
732
733     } catch (SQLException e) {
734       log.log(Level.FINE, e.toString(), e);
735       return false;
736     }
737   }
738
739   /**
740    * Set the current SQL warnings.
741    *
742    * @param warnings the new SQL warnings
743    */

744   protected void setWarnings(SQLWarning warnings)
745   {
746     _warnings = warnings;
747   }
748
749   protected void clearErrors()
750   {
751     _errorMessage = null;
752     _errorCode = 0;
753     _warnings = null;
754   }
755
756   protected void saveErrors(SQLException e)
757   {
758     _errorMessage = e.getMessage();
759     _errorCode = e.getErrorCode();
760   }
761
762   static class TableKey {
763     private final String JavaDoc _url;
764     private final String JavaDoc _catalog;
765     private final String JavaDoc _schema;
766     private final String JavaDoc _table;
767
768     TableKey(String JavaDoc url, String JavaDoc catalog, String JavaDoc schema, String JavaDoc table)
769     {
770       _url = url;
771       _catalog = catalog;
772       _schema = schema;
773       _table = table;
774     }
775
776     public int hashCode()
777     {
778       int hash = 37;
779
780       if (_catalog != null)
781         hash = 65537 * hash + _catalog.hashCode();
782
783       if (_schema != null)
784         hash = 65537 * hash + _schema.hashCode();
785
786       if (_table != null)
787         hash = 65537 * hash + _table.hashCode();
788
789       return hash;
790     }
791
792     public boolean equals(Object JavaDoc o)
793     {
794       if (this == o)
795         return true;
796       else if (! (o instanceof TableKey))
797         return false;
798
799       TableKey key = (TableKey) o;
800
801       if (_url != key._url)
802         return false;
803
804       if ((_catalog == null) != (key._catalog == null))
805         return false;
806       else if (_catalog != null && ! _catalog.equals(key._catalog))
807         return false;
808
809       if ((_schema == null) != (key._schema == null))
810         return false;
811       else if (_schema != null && ! _schema.equals(key._schema))
812         return false;
813
814       if ((_table == null) != (key._table == null))
815         return false;
816       else if (_table != null && ! _table.equals(key._table))
817         return false;
818
819       return true;
820     }
821   }
822 }
823
824
Popular Tags