Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/tripi/asp/ADODB/Connection.java


1   /**
2    * ArrowHead ASP Server 
3    * This is a source file for the ArrowHead ASP Server - an 100% Java
4    * VBScript interpreter and ASP server.
5    *
6    * For more information, see http://www.tripi.com/arrowhead
7    *
8    * Copyright (C) 2002  Terence Haddock
9    *
10   * This program is free software; you can redistribute it and/or modify
11   * it under the terms of the GNU General Public License as published by
12   * the Free Software Foundation; either version 2 of the License, or
13   * (at your option) any later version.
14   *
15   * This program is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   * GNU General Public License for more details.
19   *
20   * You should have received a copy of the GNU General Public License
21   * along with this program; if not, write to the Free Software
22   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23   *
24   */
25  package com.tripi.asp.ADODB;
26  
27  import java.sql.SQLException;
28  import java.util.Vector;
29  import java.util.Enumeration;
30  import jregex.*;
31  
32  import com.tripi.asp.*;
33  
34  import org.apache.log4j.Category;
35  
36  /**
37   * This class represents the ADODB.Connection object. This object handles
38   * connections to the database.
39   * Implementation status:
40   * <ul>
41   * <li>Attributes - Implemented.
42   * <li>CommandTimeout - Implemented, but ignored.
43   * <li>ConnectionString - Implemented.
44   * <li>ConnectionTimeout - Implemented, but ignored.
45   * <li>CursorLocation - Implemented, but ignored.
46   * <li>DefaultDatabase - Implemented, but ignored, does not make sense in JDBC
47   * <li>Errors - Implemented
48   * <li>IsolationLevel - <b>TODO</b> Not implemented
49   * <li>Mode - <b>TODO</b> Not implemented
50   * <li>Properties - <b>TODO</b> Not implemented.
51   * <li>Provider - <b>TODO</b> Not implemented
52   * <li>State - Implemented
53   * <li>Version - <b>TODO</b> Not implemented
54   * <li>BeginTrans - Implemented
55   * <li>CommitTrans - Implemented
56   * <li>RollbackTrans - Implemented
57   * <li>Close - Implemented
58   * <li>Execute - Implemented
59   * <li>Open - Implemented
60   * <li>OpenSchema - <b>TODO</b> Not implemented
61   * </ul>
62   * @author Terence Haddock
63   */
64  public class Connection
65  {
66      /** Debugging category. */
67      Category DBG = Category.getInstance(Connection.class);
68  
69      /** Current java.sql.Connection object. */
70      java.sql.Connection cx = null;
71  
72      /** Are we in a transaction? */
73      protected boolean inTransaction = false;
74      
75      /**
76       * Constructor, no arguments.
77       */
78      public Connection()
79      {
80      }
81  
82      /**
83       * What to do on page end.
84       */
85      public void OnPageEnd(Object ignoreme)
86      {
87          if (DBG.isDebugEnabled()) DBG.debug("OnPageEnd called");
88          try {
89              /* Close the connection */
90              Close();
91          } catch (AspException ex)
92          {
93              DBG.error("Error on ADODB.Connection.OnPageEnd", ex);
94          }
95      }
96  
97      /** List of errors thrown by this class. */
98      public ErrorsClass Errors = new ErrorsClass();
99  
100     /** Attributes of this connection */
101     public AspCollection Attributes = new AspCollection();
102 
103     /** Timeout for commands */
104     public int CommandTimeout;
105 
106     /** Current connection string */
107     public String ConnectionString = null;
108 
109     /** Timeout for connections */
110     public int ConnectionTimeout;
111 
112     /** Cursor location, server or client. */
113     public int CursorLocation = 2;
114 
115     /** Default database. */
116     public int DefaultDatabase;
117 
118     /** Isolation level. */
119     public int IsolationLevel;
120 
121     /** Mode for modifying data. */
122     public int Mode;
123 
124     /** Properties list. */
125     public AspCollection Properties = new AspCollection();
126 
127     /** Provider information */
128     public String Provider;
129 
130     /**
131      * State, open or closed.
132      * @return state of connection
133      */
134     public int State()
135     {
136         if (cx == null)
137             return 0;
138         else
139             return 1;
140     }
141 
142     /**
143      * Obtains the ASP Version.
144      * @return ASP Version, currently 2.5
145      */
146     public String Version()
147     {
148         return "2.5";
149     }
150 
151     /**
152      * This sub-class is used to return multiple arguments from the
153      * parseConnectionInfo(String) function.
154      */
155     static class ConnectionInfo
156     {
157         /** The JDBC driver */
158         String driver = null;
159         /** The JDBC URL */
160         String url = null;
161         /** The username */
162         String username = null;
163         /** The password */
164         String password = null;
165     }
166 
167     /**
168      * This function parses the given URL into parts. It supports the
169      * follow formats:
170      * <ul>
171      * <li>jdbc:<URL> - JDBC URLs
172      * <li>driver=<driver>;URL=<url>;UID=<user>;PWD=<password>
173      *   <ul>
174      *   <li>driver - JDBC driver class, such as org.postgresql.Driver
175      *   <li>URL - JDBC URL, such as jdbc:postgres://database
176      *   <li>UID - Username (optional)
177      *   <li>PWD - Password (optional)
178      *   </ul>
179      * </ul>
180      * @param url URL to parse
181      * @return Connection info
182      */
183     private ConnectionInfo parseConnectionInfo(String url)
184     {
185         ConnectionInfo cinfo = new ConnectionInfo();
186         if (url.startsWith("jdbc:"))
187         {
188             cinfo.url = url;
189             return cinfo;
190         }
191         final Pattern dpat = new Pattern("(^|;)(driver)=([^;]+)(;|$)",
192                 REFlags.IGNORE_CASE);
193         Matcher dmat = dpat.matcher(url);
194         if (dmat.find()) cinfo.driver = dmat.group(3);
195         final Pattern rpat = new Pattern("(^|;)(url)=([^;]+)(;|$)",
196                 REFlags.IGNORE_CASE);
197         Matcher rmat = rpat.matcher(url);
198         if (rmat.find()) cinfo.url = rmat.group(3);
199         final Pattern upat = new Pattern("(^|;)(user|uid|user id)=([^;]+)(;|$)",
200                 REFlags.IGNORE_CASE);
201         Matcher umat = upat.matcher(url);
202         if (umat.find()) cinfo.username = umat.group(3);
203         final Pattern ppat = new Pattern("(^|;)(password|pwd)=([^;]+)(;|$)",
204                 REFlags.IGNORE_CASE);
205         Matcher pmat = ppat.matcher(url);
206         if (pmat.find()) cinfo.password = pmat.group(3);
207 
208         if (DBG.isDebugEnabled())
209         {
210             DBG.debug("Driver: " + cinfo.driver);
211             DBG.debug("URL: " + cinfo.url);
212             DBG.debug("User: " + cinfo.username);
213             DBG.debug("Password: " + cinfo.password);
214         }
215         return cinfo;
216     }
217 
218     /**
219      * Open a URL with username and password.
220      * @param url URL to open   
221      * @param username Username to use
222      * @param password Password to use
223      * @throws AspException on error    
224      */
225     public void Open(String url, String username, String password)
226         throws AspException
227     {
228         this.ConnectionString = url;
229         ConnectionInfo cinfo = parseConnectionInfo(url);
230         try {
231             if (cinfo.driver != null)
232             try {
233                 Class.forName(cinfo.driver).newInstance();
234             } catch (Exception ex) {
235                 throw new AspNestedException(ex);
236             }
237             if (username != null) cinfo.username = username;
238             if (password != null) cinfo.password = password;
239             /* Close any open connections */
240             if (cx != null) Close();
241             if (DBG.isDebugEnabled())
242             {
243                 DBG.debug("cinfo.url = " + cinfo.url);
244                 DBG.debug("cinfo.username = " + cinfo.username);
245                 DBG.debug("cinfo.password = " + cinfo.password);
246             }
247             if (cinfo.username == null && cinfo.password == null)
248             {
249                 cx = java.sql.DriverManager.getConnection(cinfo.url);
250             } else {
251                 cx = java.sql.DriverManager.getConnection(cinfo.url,
252                         cinfo.username, cinfo.password);
253             }
254         } catch (SQLException ex) {
255             throw processException(ex);
256         }
257     }
258 
259     /**
260      * Open a database connection, giving only an URL.
261      * @param url URL of database to open
262      * @throws AspException on error
263      */
264     public void Open(String url) throws AspException
265     {
266         Open(url, null, null);
267     }
268 
269     /**
270      * Open a database using the ConnectionString parameter as the URL.
271      * @throws AspException on error
272      */
273     public void Open() throws AspException
274     {
275         Open(ConnectionString, null, null);
276     }
277 
278     /**
279      * Begin a transaction. Returns the transaction level for nested
280      * transactions (not implemented).
281      * @return transaction level (always 1 for this implementation)
282      * @throws AspException on error, throws exception if called while
283      *  already in a transaction.
284      */
285     public int BeginTrans() throws AspException
286     {
287         try {
288             if (inTransaction)
289             {
290                 throw new AspNotImplementedException("Nested transactions not supported");
291             }
292             cx.setAutoCommit(false);
293             inTransaction = true;
294         } catch (SQLException ex) {
295             throw processException(ex);
296         }
297         return 1;
298     }
299 
300     /**
301      * Commits the current, active transaction, throws an exception if called
302      * outside of a transaction.
303      * @throws AspException on error
304      */
305     public void CommitTrans() throws AspException
306     {
307         try {
308             if (!inTransaction)
309             {
310                 throw new AspNotImplementedException("Commit called outside of transaction.");
311             }
312             cx.commit();
313         } catch (SQLException ex) {
314             throw processException(ex);
315         } finally {
316             try {
317                 cx.setAutoCommit(true);
318             } catch (SQLException ex)
319             {
320                 throw processException(ex);
321             }
322             inTransaction = false;
323         }
324     }
325 
326     /**
327      * Rolls back the current, active transaction, throws an exception if called
328      * outside of a transaction.
329      * @throws AspException on error
330      */
331     public void RollbackTrans() throws AspException
332     {
333         try {
334             if (!inTransaction)
335             {
336                 throw new AspNotImplementedException("Commit called outside of transaction.");
337             }
338             cx.rollback();
339         } catch (SQLException ex) {
340             throw processException(ex);
341         } finally {
342             try {
343                 cx.setAutoCommit(false);
344             } catch (SQLException ex) {
345                 throw processException(ex);
346             }
347             
348             inTransaction = false;
349         }
350     }
351 
352     /**
353      * Close the current, open database connection.
354      * @throws AspException on SQL exception
355      */
356     public void Close() throws AspException
357     {
358         if (cx == null) return;
359         try {
360             cx.close();
361         } catch (SQLException ex) {
362             throw processException(ex);
363         } finally {
364             cx = null;
365         }
366     }
367 
368     /**
369      * Execute the given SQL statement.
370      * @param sql SQL expression to execute
371      * @return Record of results
372      * @throws AspException on SQL exception
373      */
374     public RecordSet Execute(String sql) throws AspException
375     {
376         return Execute(sql, null);
377     }
378 
379     /**
380      * Executes the given SQL statement, returning a result value.
381      * @param sql SQL expression to execute
382      * @param recordsEffected by-reference variable which will contain the
383      * number of records modified.
384      * @return Record of results
385      * @throws AspException on SQL exception
386      */
387     public RecordSet Execute(String sql, ByRefValue recordsAffected)
388         throws AspException
389     {
390         return Execute(sql, recordsAffected, 0);
391     }
392 
393     /**
394      * Executes the given SQL statement, returning a result value.
395      * @param sql SQL expression to execute
396      * @param recordsEffected by-reference variable which will contain the
397      * number of records modified.
398      * @param flags ADODB flags
399      * @return Record of results
400      * @throws AspException on SQL exception
401      */
402     public RecordSet Execute(String sql, ByRefValue recordsAffected, int flags)
403         throws AspException
404     {
405         RecordSet rc = new RecordSet();
406         Command cmd = new Command();
407         cmd.CommandText = sql;
408         cmd.ActiveConnection(this);
409 
410         int res = rc.Open(cmd, null, 0, 0);
411         if ((recordsAffected != null) && (res != -1))
412         {
413             Integer ra = new Integer(res);
414             recordsAffected.setValue(ra);
415         }
416 
417         return rc;
418     }
419 
420     /**
421      * Process this exception, add it to the error list and re-throw it
422      * as an AspException. Package scope on purpose.
423      * @param ex Exception to process
424      */
425     AspException processException(Throwable ex)
426     {
427         return processException(ex, 0x80004005);
428     }
429 
430     /**
431      * Process this exception, add it to the error list and re-throw it
432      * as an AspException. Package scope on purpose.
433      * @param ex Exception to process
434      * @param errorCode error code
435      */
436     AspException processException(Throwable ex, int errorCode)
437     {
438         AspException aspEx;
439         if (ex instanceof AspException)
440         {
441             aspEx = (AspException)ex;
442         } else {
443             aspEx = new AspNestedException(ex, errorCode);
444         }
445         Errors.AddError(aspEx);
446         return aspEx;
447     }
448 
449     /**
450      * This inner class contains the list of SQL errors.
451      */
452     static public class ErrorsClass implements SimpleMap
453     {
454         /** Debugging context */
455         static Category DBG = Category.getInstance(ErrorsClass.class);
456 
457         /** Internal list of SQL errors. */
458         protected Vector _errorlist = new Vector();
459 
460         /**
461          * Returns number of errors in list.
462          * @return number of SQL errors
463          */
464         public int Count()
465         {
466             return _errorlist.size();
467         }
468 
469         /**
470          * Clears the SQL errors
471          */
472         public void Clear()
473         {
474             _errorlist = new Vector();
475         }
476     
477         /**
478          * Internal function to add an error to the list
479          * @param ex Exception to add to list
480          */
481         protected void AddError(AspException ex)
482         {
483             if (DBG.isDebugEnabled())
484                 DBG.debug("AddError: " + ex);
485             _errorlist.add(ex);
486         }
487 
488         /**
489          * Obtain the error at the specified index.
490          * @param obj Object to be coerced into integer
491          * @return Error at the specified index.
492          * @throws AspException on error
493          */
494         public Object get(Object obj) throws AspException
495         {
496             int i = Types.coerceToInteger(obj).intValue();
497 
498             return _errorlist.get(i);
499         }
500 
501         /**
502          * The Errors list is read-only, so this function will throw
503          * an error.
504          * @param obj Index
505          * @param value Value
506          * @throws AspException always throws read only exception
507          */
508         public void put(Object obj, Object value) throws AspException
509         {
510             throw new AspReadOnlyException("Errors");
511         }
512 
513         /**
514          * Function to obtain keys in this collection. This function should
515          * not be called.
516          * @return enumeration of keys
517          */
518         public Enumeration getKeys()
519         {
520             return null;
521         }
522     }
523 }
524