DBHelper.java
001 /*
002  *  DBHelper.java
003  *
004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Marin Dimitrov, 18/Sep/2001
013  *
014  *  $Id: DBHelper.java 12006 2009-12-01 17:24:28Z thomas_heitz $
015  */
016 
017 package gate.persist;
018 
019 import java.sql.*;
020 import java.util.HashMap;
021 
022 import gate.Gate;
023 
024 public class DBHelper {
025 
026   /** class name of the Oracle jdbc driver */
027   private static final String jdbcOracleDriverName 
028       "oracle.jdbc.driver.OracleDriver";
029   private static final String jdbcPostgresDriverName 
030       "org.postgresql.Driver";
031 
032   public static final int CHINK_SIZE_SMALL = 30;
033   public static final int CHINK_SIZE_MEDIUM = 60;
034   public static final int CHINK_SIZE_LARGE = 100;
035 
036   //WARNING!
037   //DO NOT EDIT THESE CONSTANTS WITHOUT
038   //SYNCHRONIZING WITH ERROR.SPC PL/SQL PACKAGE
039   //note that while Oracle returns negative error numbers
040   //the SQLException::getErrorCode() returns positive ones
041   //
042 
043   /** user defined error codes in Oracle start with -21000 */
044   public static final int X_ORACLE_START = 20100;
045 
046   /**  this should be thrown if an attempt to create a group with duplicated name is made */
047   public static final int X_ORACLE_DUPLICATE_GROUP_NAME =      X_ORACLE_START + ;
048 
049   /** see above */
050   public static final int X_ORACLE_DUPLICATE_USER_NAME =       X_ORACLE_START + ;
051 
052   /** no such user failure upon login */
053   public static final int X_ORACLE_INVALID_USER_NAME =         X_ORACLE_START + ;
054 
055   /** - */
056   public static final int X_ORACLE_INVALID_USER_PASS =         X_ORACLE_START + ;
057 
058   /** invalid group id supplied for operation requiring such specifier */
059   public static final int X_ORACLE_INVALID_USER_GROUP =        X_ORACLE_START + ;
060 
061   /** access to LR by id fails - no such resource */
062   public static final int X_ORACLE_INVALID_LR =                X_ORACLE_START + ;
063 
064   /** attempt to access resource in mode that does not exist */
065   public static final int X_ORACLE_INVALID_ACCESS_MODE =       X_ORACLE_START + ;
066 
067   /** huh? */
068   public static final int X_ORACLE_INVALID_ARGUMENT =          X_ORACLE_START + ;
069 
070   /** this should not be in use anymore */
071   public static final int X_ORACLE_NOT_IMPLEMENTED =           X_ORACLE_START + ;
072 
073   /** attempt to delete a group that owns resources is made */
074   public static final int X_ORACLE_GROUP_OWNS_RESOURCES =      X_ORACLE_START + 10 ;
075 
076   /** attempt to delete a user that owns resources is made */
077   public static final int X_ORACLE_USER_OWNS_RESOURCES =       X_ORACLE_START + 11 ;
078 
079   /** huh? */
080   public static final int X_ORACLE_INCOMPLETE_DATA  =          X_ORACLE_START + 12 ;
081 
082   /** attempt to access resources by type is made, but no such type exists */
083   public static final int X_ORACLE_INVALID_LR_TYPE  =          X_ORACLE_START + 13 ;
084 
085   /** this is obsolete now? */
086   public static final int X_ORACLE_INVALID_ANNOTATION_TYPE =   X_ORACLE_START + 14 ;
087 
088   /** attempt to create a feature with invalid value type is made
089    *  since value types are automatically assigned in the java code, this errror
090    *  should indicate that the java code was changed but no changes were made to the
091    *  relevant pl/sql code
092    *  */
093   public static final int X_ORACLE_INVALID_FEATURE_TYPE =      X_ORACLE_START + 15 ;
094 
095   /**
096    * not supported content type - we support only character/binary/empty content
097    * since there are no many other options this error shoudkl indicate that the
098    * java code was not synced with the pl/sql one
099    *
100    *  */
101   public static final int X_ORACLE_INVALID_CONTENT_TYPE =      X_ORACLE_START + 16 ;
102 
103   /** attempt to remove annotation that does not exist is made */
104   public static final int X_ORACLE_INVALID_ANNOTATION =        X_ORACLE_START + 17 ;
105 
106   /** attempt to perform an operation that requres more privileged is made */
107   public static final int X_ORACLE_INSUFFICIENT_PRIVILEGES =   X_ORACLE_START + 18 ;
108 
109   /** attempt to remove annotation set that does not exist is made */
110   public static final int X_ORACLE_INVALID_ANNOTATION_SET  =   X_ORACLE_START + 19 ;
111 
112   public static final int TRUE = 1;
113   public static final int FALSE = 0;
114 
115   /** character content (may make difference for the database) */
116   public static final int CHARACTER_CONTENT = 1;
117 
118   /** binary content (may make difference for the database) */
119   public static final int BINARY_CONTENT = 2;
120 
121   /** document has no content*/
122   public static final int EMPTY_CONTENT = 3;
123 
124   /** LR classes supported at present */
125   public static final String DOCUMENT_CLASS = "gate.corpora.DatabaseDocumentImpl";
126   /** LR classes supported at present */
127   public static final String CORPUS_CLASS =  "gate.corpora.DatabaseCorpusImpl";
128 
129   /** key in T_PARAMETER that defines a unique id for the data store */
130   public static final String  DB_PARAMETER_GUID = "DB_GUID";
131 
132   //dummy key
133   //hopefully no one will create a feature with such key
134   /** dummy feature key, do not use it */
135   public static final String DUMMY_FEATURE_KEY =  "--NO--SUCH--KEY--";
136   /** dummy encoding type, do not use it */
137   public static final String DUMMY_ENCODING =  "-!-";
138 
139   /** used internaly, may change in the future */
140   public static final int READ_ACCESS = 0;
141   /** used internaly, may change in the future */
142   public static final int WRITE_ACCESS = 1;
143 
144   //dummy ID
145   /** huh? */
146   public static final Long DUMMY_ID;
147 
148 
149   //!!! WARNING !!!
150   // these 4 constants should *always* be synchronzied with the ones in the
151   // related SQL packages/scripts [for Oracle - security.spc]
152   // i.e. if u don't have a serious reason do *not* change anything
153 
154   /** used to store corpus' features */
155   protected static final int FEATURE_OWNER_CORPUS  = 1;
156   /** used to store document's features */
157   protected static final int FEATURE_OWNER_DOCUMENT  = 2;
158   /** used to store annotation's features */
159   protected static final int FEATURE_OWNER_ANNOTATION  = 3;
160 
161   /** feature value is null  */
162   public static final int VALUE_TYPE_NULL              = 100;
163   /** feature value is int  */
164   public static final int VALUE_TYPE_INTEGER           = 101;
165   /** feature value is long */
166   public static final int VALUE_TYPE_LONG              = 102;
167   /** feature value is boolean */
168   public static final int VALUE_TYPE_BOOLEAN           = 103;
169   /** feature value is string less than 4000 bytes */
170   public static final int VALUE_TYPE_STRING            = 104;
171   /** feature value is binary */
172   public static final int VALUE_TYPE_BINARY            = 105;
173   /** feature value is float */
174   public static final int VALUE_TYPE_FLOAT             = 106;
175   /** feature value is array of ints */
176   public static final int VALUE_TYPE_INTEGER_ARR       = 107;
177   /** feature value is array of longs */
178   public static final int VALUE_TYPE_LONG_ARR          = 108;
179   /** feature value is array of bools */
180   public static final int VALUE_TYPE_BOOLEAN_ARR       = 109;
181   /** feature value is array of strings */
182   public static final int VALUE_TYPE_STRING_ARR        = 110;
183   /** feature value is array of binary values */
184   public static final int VALUE_TYPE_BINARY_ARR        = 111;
185   /** feature value is array of floats */
186   public static final int VALUE_TYPE_FLOAT_ARR         = 112;
187   /** feature value is array of floats */
188   public static final int VALUE_TYPE_EMPTY_ARR         = 113;
189 
190   /** Oracle database type */
191   public static final int ORACLE_DB = 101;
192   /** PostgreSQL database type */
193   public static final int POSTGRES_DB = 102;
194 
195   private static final boolean DEBUG = false;
196 
197     private static boolean oracleLoaded = false;
198     private static boolean postgresLoaded = false;
199   private static HashMap pools;
200 
201   /** size (in elements) of the jdbc connection pool (if any) */
202   private static final int POOL_SIZE = 20;
203 
204   static {
205     DUMMY_ID = new Long(Long.MIN_VALUE);
206     pools = new HashMap();
207   }
208 
209 
210   protected DBHelper() {
211 
212     //no way
213     //contains only static methods
214   }
215 
216   /** --- */
217   private static synchronized void loadDrivers(final int dbType)
218     throws ClassNotFoundException {
219 
220       if (!oracleLoaded && dbType == ORACLE_DB) {
221     Class.forName(jdbcOracleDriverName);
222       else if (!postgresLoaded && dbType == POSTGRES_DB) {
223     Class.forName(jdbcPostgresDriverName);
224       }
225   }
226 
227 
228   /**
229    *  closes a result set
230    *  note that Oracle jdbc classes do not have finalize() implementations so if
231    *  they're not closed leaks may occur
232    */
233   public static void cleanup(ResultSet rs)
234     throws PersistenceException {
235 
236     try {
237       if (rs!=null)
238         rs.close();
239     }
240     catch(SQLException sqle) {
241       throw new PersistenceException("an SQL exception occured ["+ sqle.getMessage()+"]");
242     }
243   }
244 
245   /**
246    *  closes a statement
247    *  note that Oracle jdbc classes do not have finalize() implementations so if
248    *  they're not closed leaks may occur
249    */
250   public static void cleanup(Statement stmt)
251     throws PersistenceException {
252     try {
253       if (stmt!=null)
254         stmt.close();
255     }
256     catch(SQLException sqle) {
257       throw new PersistenceException("an SQL exception occured ["+ sqle.getMessage()+"]");
258     }
259   }
260 
261   /**
262    *  connects to DB
263    */
264   public static Connection connect(final String connectURL)
265     throws SQLException,ClassNotFoundException{
266 
267     loadDriversgetDatabaseType(connectURL) );
268     Connection conn = DriverManager.getConnection(connectURL);
269 
270     if (DEBUG) {
271       DatabaseMetaData meta = conn.getMetaData();
272       gate.util.Out.println(
273             "JDBC driver name=["+meta.getDriverName() +
274             "] version=["+ meta.getDriverVersion() +"]");
275     }
276 
277     return conn;
278   }
279 
280   /**
281    *  connects to DB
282    */
283   public static Connection connect(final String connectURL, 
284            final String user, 
285            final String pass)
286     throws SQLException,ClassNotFoundException{
287 
288     loadDriversgetDatabaseType(connectURL) );
289     Connection conn = DriverManager.getConnection(connectURL, user, pass);
290 
291     if (DEBUG) {
292       DatabaseMetaData meta = conn.getMetaData();
293       gate.util.Err.println(
294             "JDBC driver name=["+meta.getDriverName() +
295             "] version=["+ meta.getDriverVersion() +"]");
296     }
297 
298     return conn;
299   }
300 
301   /**
302    * disconnects from DB, may return connection to pool if such exists
303    *
304    * any uncommited transactions are rolled back
305    */
306   public static void disconnect(Connection conn)
307     throws PersistenceException{
308 
309     //2. close the JDBC connection
310     try {
311       //rollback uncommited transactions
312       conn.rollback();
313       conn.close();
314     }
315     catch (SQLException sqle) {
316       throw new PersistenceException("cannot close JDBC connection, DB error is ["+
317                                       sqle.getMessage() +"]");
318     }
319   }
320 
321   /**
322    *  connects to DB
323    * gets connection from pool if such exists
324    */
325   public static Connection connect(String connectURL,boolean usePool)
326     throws SQLException,ClassNotFoundException{
327 
328     if (false == usePool) {
329       return connect(connectURL);
330     }
331     else {
332       ConnectionPool currPool = null;
333 
334       synchronized(pools) {
335         if (false == pools.containsKey(connectURL)) {
336           currPool = new ConnectionPool(POOL_SIZE, connectURL);
337           pools.put(connectURL, currPool);
338         }
339         else {
340           currPool = (ConnectionPoolpools.get(connectURL);
341         }
342       }
343 
344       return currPool.get();
345     }
346   }
347 
348   /**
349    * disconnects from DB, may return connection to pool if such exists
350    *
351    * any uncommited transactions are rolled back
352    */
353   public static void disconnect(Connection conn, boolean usePool)
354     throws PersistenceException{
355 
356     if (false == usePool) {
357       disconnect(conn);
358     }
359     else {
360       String jdbcURL = null;
361 
362       try {
363         jdbcURL = conn.getMetaData().getURL();
364         conn.rollback();
365       }
366       catch(SQLException sqle) {
367         throw new PersistenceException(sqle);
368       }
369 
370       ConnectionPool currPool = (ConnectionPoolpools.get(jdbcURL);
371       currPool.put(conn);
372     }
373   }
374 
375   public static String getSchemaPrefix(String jdbcURL) {
376 
377     if (jdbcURL.startsWith("jdbc:oracle")) {
378       return Gate.DB_OWNER+".";
379     }
380     else if (jdbcURL.startsWith("jdbc:postgres")) {
381       return "";
382     }
383     else {
384       throw new IllegalArgumentException();
385     }
386   }
387 
388   public static int getDatabaseType(String jdbcURL) {
389 
390     if (jdbcURL.startsWith("jdbc:oracle")) {
391       return DBHelper.ORACLE_DB;
392     }
393     else if (jdbcURL.startsWith("jdbc:postgres")) {
394       return DBHelper.POSTGRES_DB;
395     }
396     else {
397       throw new IllegalArgumentException();
398     }
399   }
400 
401 }