1   /*
2    *  DBHelper.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Marin Dimitrov, 18/Sep/2001
12   *
13   *  $Id: DBHelper.java,v 1.30 2002/03/07 15:18:51 marin Exp $
14   */
15  
16  package gate.persist;
17  
18  import java.sql.*;
19  import java.net.*;
20  import java.util.*;
21  
22  import gate.util.*;
23  
24  public class DBHelper {
25  
26    /** class name of the Oracle jdbc driver */
27    private static final String jdbcOracleDriverName = "oracle.jdbc.driver.OracleDriver";
28  //  private static final String jdbcPostgresDriverName = "postgresql.Driver";
29  //  private static final String jdbcSapDBDriverName = "com.sap.dbtech.jdbc.DriverSapDB";
30  
31    public static final int CHINK_SIZE_SMALL = 30;
32    public static final int CHINK_SIZE_MEDIUM = 60;
33    public static final int CHINK_SIZE_LARGE = 100;
34    //WARNING!
35    //DO NOT EDIT THESE CONSTANTS WITHOUT
36    //SYNCHRONIZING WITH ERROR.SPC PL/SQL PACKAGE
37    //note that while Oracle returns negative error numbers
38    //the SQLException::getErrorCode() returns positive ones
39    //
40  
41    /** user defined error codes in Oracle start with -21000 */
42    public static final int X_ORACLE_START = 20100;
43  
44    /**  this should be thrown if an attempt to create a group with duplicated name is made */
45    public static final int X_ORACLE_DUPLICATE_GROUP_NAME =      X_ORACLE_START + 1 ;
46  
47    /** see above */
48    public static final int X_ORACLE_DUPLICATE_USER_NAME =       X_ORACLE_START + 2 ;
49  
50    /** no such user failure upon login */
51    public static final int X_ORACLE_INVALID_USER_NAME =         X_ORACLE_START + 3 ;
52  
53    /** - */
54    public static final int X_ORACLE_INVALID_USER_PASS =         X_ORACLE_START + 4 ;
55  
56    /** invalid group id supplied for operation requiring such specifier */
57    public static final int X_ORACLE_INVALID_USER_GROUP =        X_ORACLE_START + 5 ;
58  
59    /** access to LR by id fails - no such resource */
60    public static final int X_ORACLE_INVALID_LR =                X_ORACLE_START + 6 ;
61  
62    /** attempt to access resource in mode that does not exist */
63    public static final int X_ORACLE_INVALID_ACCESS_MODE =       X_ORACLE_START + 7 ;
64  
65    /** huh? */
66    public static final int X_ORACLE_INVALID_ARGUMENT =          X_ORACLE_START + 8 ;
67  
68    /** this should not be in use anymore */
69    public static final int X_ORACLE_NOT_IMPLEMENTED =           X_ORACLE_START + 9 ;
70  
71    /** attempt to delete a group that owns resources is made */
72    public static final int X_ORACLE_GROUP_OWNS_RESOURCES =      X_ORACLE_START + 10 ;
73  
74    /** attempt to delete a user that owns resources is made */
75    public static final int X_ORACLE_USER_OWNS_RESOURCES =       X_ORACLE_START + 11 ;
76  
77    /** huh? */
78    public static final int X_ORACLE_INCOMPLETE_DATA  =          X_ORACLE_START + 12 ;
79  
80    /** attempt to access resources by type is made, but no such type exists */
81    public static final int X_ORACLE_INVALID_LR_TYPE  =          X_ORACLE_START + 13 ;
82  
83    /** this is obsolete now? */
84    public static final int X_ORACLE_INVALID_ANNOTATION_TYPE =   X_ORACLE_START + 14 ;
85  
86    /** attempt to create a feature with invalid value type is made
87     *  since value types are automatically assigned in the java code, this errror
88     *  should indicate that the java code was changed but no changes were made to the
89     *  relevant pl/sql code
90     *  */
91    public static final int X_ORACLE_INVALID_FEATURE_TYPE =      X_ORACLE_START + 15 ;
92  
93    /**
94     * not supported content type - we support only character/binary/empty content
95     * since there are no many other options this error shoudkl indicate that the
96     * java code was not synced with the pl/sql one
97     *
98     *  */
99    public static final int X_ORACLE_INVALID_CONTENT_TYPE =      X_ORACLE_START + 16 ;
100 
101   /** attempt to remove annotation that does not exist is made */
102   public static final int X_ORACLE_INVALID_ANNOTATION =        X_ORACLE_START + 17 ;
103 
104   /** attempt to perform an operation that requres more privileged is made */
105   public static final int X_ORACLE_INSUFFICIENT_PRIVILEGES =   X_ORACLE_START + 18 ;
106 
107   /** attempt to remove annotation set that does not exist is made */
108   public static final int X_ORACLE_INVALID_ANNOTATION_SET  =   X_ORACLE_START + 19 ;
109 
110   public static final int TRUE = 1;
111   public static final int FALSE = 0;
112 
113   /** character content (may make difference for the database) */
114   public static final int CHARACTER_CONTENT = 1;
115 
116   /** binary content (may make difference for the database) */
117   public static final int BINARY_CONTENT = 2;
118 
119   /** document has no content*/
120   public static final int EMPTY_CONTENT = 3;
121 
122   /** LR classes supported at present */
123   public static final String DOCUMENT_CLASS = "gate.corpora.DatabaseDocumentImpl";
124   /** LR classes supported at present */
125   public static final String CORPUS_CLASS =  "gate.corpora.DatabaseCorpusImpl";
126 
127   /** key in T_PARAMETER that defines a unique id for the data store */
128   public static final String  DB_PARAMETER_GUID = "DB_GUID";
129 
130   //dummy key
131   //hopefully no one will create a feature with such key
132   /** dummy feature key, do not use it */
133   public static final String DUMMY_FEATURE_KEY =  "--NO--SUCH--KEY--";
134   /** dummy encoding type, do not use it */
135   public static final String DUMMY_ENCODING =  "-!-";
136 
137   //dummy ID
138   /** huh? */
139   public static final Long DUMMY_ID;
140 
141 
142   //!!! WARNING !!!
143   // these 4 constants should *always* be synchronzied with the ones in the
144   // related SQL packages/scripts [for Oracle - security.spc]
145   // i.e. if u don't have a serious reason do *not* change anything
146 
147   /** used to store corpus' features */
148   protected static final int FEATURE_OWNER_CORPUS  = 1;
149   /** used to store document's features */
150   protected static final int FEATURE_OWNER_DOCUMENT  = 2;
151   /** used to store annotation's features */
152   protected static final int FEATURE_OWNER_ANNOTATION  = 3;
153 
154   /** feature value is null  */
155   public static final int VALUE_TYPE_NULL              = 100;
156   /** feature value is int  */
157   public static final int VALUE_TYPE_INTEGER           = 101;
158   /** feature value is long */
159   public static final int VALUE_TYPE_LONG              = 102;
160   /** feature value is boolean */
161   public static final int VALUE_TYPE_BOOLEAN           = 103;
162   /** feature value is string less than 4000 bytes */
163   public static final int VALUE_TYPE_STRING            = 104;
164   /** feature value is binary */
165   public static final int VALUE_TYPE_BINARY            = 105;
166   /** feature value is float */
167   public static final int VALUE_TYPE_FLOAT             = 106;
168   /** feature value is array of ints */
169   public static final int VALUE_TYPE_INTEGER_ARR       = 107;
170   /** feature value is array of longs */
171   public static final int VALUE_TYPE_LONG_ARR          = 108;
172   /** feature value is array of bools */
173   public static final int VALUE_TYPE_BOOLEAN_ARR       = 109;
174   /** feature value is array of strings */
175   public static final int VALUE_TYPE_STRING_ARR        = 110;
176   /** feature value is array of binary values */
177   public static final int VALUE_TYPE_BINARY_ARR        = 111;
178   /** feature value is array of floats */
179   public static final int VALUE_TYPE_FLOAT_ARR         = 112;
180   /** feature value is array of floats */
181   public static final int VALUE_TYPE_EMPTY_ARR         = 113;
182 
183 
184   private static final boolean DEBUG = false;
185 
186   private static boolean  driversLoaded;
187   private static HashMap pools;
188 
189   /** size (in elements) of the jdbc connection pool (if any) */
190   private static final int POOL_SIZE = 20;
191 
192   static {
193     DUMMY_ID = new Long(Long.MIN_VALUE);
194     driversLoaded = false;
195     pools = new HashMap();
196   }
197 
198 
199   protected DBHelper() {
200 
201     //no way
202     //contains only static methods
203   }
204 
205   /** --- */
206   private static synchronized void loadDrivers()
207     throws ClassNotFoundException {
208 
209     if (!driversLoaded) {
210       Class.forName(jdbcOracleDriverName);
211 //      Class.forName(jdbcPostgresDriverName);
212 //      Class.forName(jdbcSapDBDriverName);
213 
214       driversLoaded = true;
215     }
216   }
217 
218 
219   /**
220    *  closes a result set
221    *  note that Oracle jdbc classes do not have finalize() implementations so if
222    *  they're not closed leaks may occur
223    */
224   public static void cleanup(ResultSet rs)
225     throws PersistenceException {
226 
227     try {
228       if (rs!=null)
229         rs.close();
230     }
231     catch(SQLException sqle) {
232       throw new PersistenceException("an SQL exception occured ["+ sqle.getMessage()+"]");
233     }
234   }
235 
236   /**
237    *  closes a statement
238    *  note that Oracle jdbc classes do not have finalize() implementations so if
239    *  they're not closed leaks may occur
240    */
241   public static void cleanup(Statement stmt)
242     throws PersistenceException {
243     try {
244       if (stmt!=null)
245         stmt.close();
246     }
247     catch(SQLException sqle) {
248       throw new PersistenceException("an SQL exception occured ["+ sqle.getMessage()+"]");
249     }
250   }
251 
252   /**
253    *  connects to DB
254    */
255   public static Connection connect(String connectURL)
256     throws SQLException,ClassNotFoundException{
257 
258     loadDrivers();
259     Connection conn = DriverManager.getConnection(connectURL);
260 
261     if (DEBUG) {
262       DatabaseMetaData meta = conn.getMetaData();
263       gate.util.Err.println(
264             "JDBC driver name=["+meta.getDriverName() +
265             "] version=["+ meta.getDriverVersion() +"]");
266     }
267 
268     return conn;
269   }
270 
271   /**
272    * disconnects from DB, may return connection to pool if such exists
273    *
274    * any uncommited transactions are rolled back
275    */
276   public static void disconnect(Connection conn)
277     throws PersistenceException{
278 
279     //2. close the JDBC connection
280     try {
281       //rollback uncommited transactions
282       conn.rollback();
283       conn.close();
284     }
285     catch (SQLException sqle) {
286       throw new PersistenceException("cannot close JDBC connection, DB error is ["+
287                                       sqle.getMessage() +"]");
288     }
289   }
290 
291   /**
292    *  connects to DB
293    * gets connection from pool if such exists
294    */
295   public static Connection connect(String connectURL,boolean usePool)
296     throws SQLException,ClassNotFoundException{
297 
298     if (false == usePool) {
299       return connect(connectURL);
300     }
301     else {
302       ConnectionPool currPool = null;
303 
304       synchronized(pools) {
305         if (false == pools.containsKey(connectURL)) {
306           currPool = new ConnectionPool(POOL_SIZE, connectURL);
307           pools.put(connectURL, currPool);
308         }
309         else {
310           currPool = (ConnectionPool) pools.get(connectURL);
311         }
312       }
313 
314       return currPool.get();
315     }
316   }
317 
318   /**
319    * disconnects from DB, may return connection to pool if such exists
320    *
321    * any uncommited transactions are rolled back
322    */
323   public static void disconnect(Connection conn, boolean usePool)
324     throws PersistenceException{
325 
326     if (false == usePool) {
327       disconnect(conn);
328     }
329     else {
330       String jdbcURL = null;
331 
332       try {
333         jdbcURL = conn.getMetaData().getURL();
334         conn.rollback();
335       }
336       catch(SQLException sqle) {
337         throw new PersistenceException(sqle);
338       }
339 
340       ConnectionPool currPool = (ConnectionPool) pools.get(jdbcURL);
341       currPool.put(conn);
342     }
343   }
344 
345 }
346