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