001package com.hfg.sql.jdbc;
002
003import java.io.IOException;
004import java.io.Reader;
005import java.io.StringReader;
006import java.sql.*;
007import java.util.List;
008import java.util.Map;
009import java.util.Properties;
010import java.util.concurrent.Executor;
011import java.util.logging.Level;
012import java.util.logging.Logger;
013
014import com.hfg.security.LoginCredentials;
015import com.hfg.sql.SQLUtil;
016import com.hfg.util.StackTraceUtil;
017
018//------------------------------------------------------------------------------
019/**
020 Represents an RDBMS database connection.
021 <div>
022 @author J. Alex Taylor, hairyfatguy.com
023 </div>
024 */
025//------------------------------------------------------------------------------
026// com.hfg XML/HTML Coding Library
027//
028// This library is free software; you can redistribute it and/or
029// modify it under the terms of the GNU Lesser General Public
030// License as published by the Free Software Foundation; either
031// version 2.1 of the License, or (at your option) any later version.
032//
033// This library is distributed in the hope that it will be useful,
034// but WITHOUT ANY WARRANTY; without even the implied warranty of
035// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
036// Lesser General Public License for more details.
037//
038// You should have received a copy of the GNU Lesser General Public
039// License along with this library; if not, write to the Free Software
040// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
041//
042// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
043// jataylor@hairyfatguy.com
044//------------------------------------------------------------------------------
045
046public abstract class JDBCConnection implements Connection
047{
048   private final Connection mInnerConn;
049   private JDBCServer       mServer;
050   private String           mDatabaseName;
051   private LoginCredentials mCredentials;
052   private JDBCConnectionSettings mSettings = new JDBCConnectionSettings();
053
054   private final static Logger LOGGER = Logger.getLogger(JDBCConnection.class.getName());
055
056   static
057   {
058      LOGGER.setLevel(Level.INFO);
059   }
060
061   //###########################################################################
062   // CONSTRUCTORS
063   //###########################################################################
064
065   //---------------------------------------------------------------------------
066   protected JDBCConnection(Connection inConnection)
067   {
068      mInnerConn = inConnection;
069   }
070
071   //---------------------------------------------------------------------------
072   protected JDBCConnection(JDBCServer inServer, String inDatabaseName, LoginCredentials inCredentials)
073   {
074      this(inServer, inDatabaseName, inCredentials, null);
075   }
076
077   //---------------------------------------------------------------------------
078   protected JDBCConnection(JDBCServer inServer, String inDatabaseName, LoginCredentials inCredentials, JDBCConnectionSettings inSettings)
079   {
080      mServer       = inServer;
081      mDatabaseName = inDatabaseName;
082      mCredentials  = inCredentials;
083      mSettings     = inSettings;
084
085      mInnerConn = establishConnection();
086   }
087
088
089   //###########################################################################
090   // PUBLIC METHODS
091   //###########################################################################
092
093   //---------------------------------------------------------------------------
094   public static Logger getLogger()
095   {
096      return LOGGER;
097   }
098
099   //---------------------------------------------------------------------------
100   public boolean tableExists(String inTablename)
101      throws SQLException
102   {
103      boolean tableExists;
104
105      DatabaseMetaData metaData = getMetaData();
106      ResultSet rs = null;
107      try
108      {
109         rs = metaData.getTables(null, null, inTablename.toLowerCase(), null);
110
111         tableExists = (rs.next()
112                        && rs.getString("TABLE_NAME").equalsIgnoreCase(inTablename));
113      }
114      finally
115      {
116         if (rs != null) rs.close();
117      }
118
119      return tableExists;
120   }
121
122   //---------------------------------------------------------------------------
123   public boolean dropTable(String inTablename)
124         throws SQLException
125   {
126      return SQLUtil.execute(this, "DROP TABLE " + inTablename);
127   }
128
129   //---------------------------------------------------------------------------
130   public abstract String getCurrentUser()
131         throws SQLException;
132
133   //---------------------------------------------------------------------------
134   public boolean execute(String inSQL)
135         throws SQLException
136   {
137      return SQLUtil.execute(this, inSQL);
138   }
139
140   //---------------------------------------------------------------------------
141   public int[] batchExecute(String inSQL)
142      throws IOException, SQLException
143   {
144      return batchExecute(new StringReader(inSQL));
145   }
146
147   //---------------------------------------------------------------------------
148   public int[] batchExecute(Reader inSQL)
149   {
150      int[] results;
151
152      try
153      {
154         Statement stmt = null;
155         try
156         {
157            stmt = createStatement();
158
159            List<String> sqlCmds = SQLUtil.splitBatchSQL(inSQL);
160            for (String sql : sqlCmds)
161            {
162               stmt.addBatch(sql);
163            }
164
165            results = stmt.executeBatch();
166         }
167         finally
168         {
169            SQLUtil.close(stmt);
170         }
171      }
172      catch (SQLException e)
173      {
174         // SQLExceptions are broken out separately so that we can expose the next exception if present.
175         throw new JDBCException(e);
176      }
177      catch (IOException e)
178      {
179         throw new JDBCException(e);
180      }
181
182      return results;
183   }
184
185   //---------------------------------------------------------------------------
186   public String name()
187   {
188      String connName = toString();
189      int index = connName.lastIndexOf(".");
190      if (index >= 0)
191      {
192         connName =  connName.substring(index+ 1);
193      }
194
195      return connName;
196   }
197
198   //---------------------------------------------------------------------------
199   @Override
200   public Statement createStatement()
201         throws SQLException
202   {
203      return mInnerConn.createStatement();
204   }
205
206   @Override
207   public PreparedStatement prepareStatement(String inSQL)
208         throws SQLException
209   {
210      return mInnerConn.prepareStatement(inSQL);
211   }
212
213   @Override
214   public CallableStatement prepareCall(String inSQL)
215         throws SQLException
216   {
217      return mInnerConn.prepareCall(inSQL);
218   }
219
220   @Override
221   public String nativeSQL(String inSQL)
222         throws SQLException
223   {
224      return mInnerConn.nativeSQL(inSQL);
225   }
226
227   @Override
228   public void setAutoCommit(boolean inAutoCommit)
229         throws SQLException
230   {
231      // Don't spend time extracting the stack trace if it won't be logged anyway
232      if (LOGGER.getLevel() != null
233          && LOGGER.getLevel().intValue() <= Level.FINE.intValue()
234          && ! StackTraceUtil.getCallingMethodName().equals("setAutoCommit")) // Avoid double logging if the connection is wrapped
235      {
236         LOGGER.fine(name() + " autocommit set from " + mInnerConn.getAutoCommit() + " to " + inAutoCommit + " by " + StackTraceUtil.getCallingStackTraceElement(null).toString());
237      }
238
239      mInnerConn.setAutoCommit(inAutoCommit);
240   }
241
242
243   @Override
244   public boolean getAutoCommit()
245         throws SQLException
246   {
247      return mInnerConn.getAutoCommit();
248   }
249
250   @Override
251   public void commit()
252         throws SQLException
253   {
254      mInnerConn.commit();
255   }
256
257   @Override
258   public void rollback()
259         throws SQLException
260   {
261      mInnerConn.rollback();
262   }
263
264   @Override
265   public void close()
266         throws SQLException
267   {
268      mInnerConn.close();
269   }
270
271   @Override
272   public boolean isClosed()
273         throws SQLException
274   {
275      return mInnerConn.isClosed();
276   }
277
278   @Override
279   public DatabaseMetaData getMetaData()
280         throws SQLException
281   {
282      return mInnerConn.getMetaData();
283   }
284
285   @Override
286   public void setReadOnly(boolean inValue)
287         throws SQLException
288   {
289      mInnerConn.setReadOnly(inValue);
290   }
291
292   @Override
293   public boolean isReadOnly()
294         throws SQLException
295   {
296      return mInnerConn.isReadOnly();
297   }
298
299   @Override
300   public void setCatalog(String inValue)
301         throws SQLException
302   {
303      mInnerConn.setCatalog(inValue);
304   }
305
306   @Override
307   public String getCatalog()
308         throws SQLException
309   {
310      return mInnerConn.getCatalog();
311   }
312
313   @Override
314   public void setTransactionIsolation(int inLevel)
315         throws SQLException
316   {
317      mInnerConn.setTransactionIsolation(inLevel);
318   }
319
320   @Override
321   public int getTransactionIsolation()
322         throws SQLException
323   {
324      return mInnerConn.getTransactionIsolation();
325   }
326
327   @Override
328   public SQLWarning getWarnings()
329         throws SQLException
330   {
331      return mInnerConn.getWarnings();
332   }
333
334   @Override
335   public void clearWarnings()
336         throws SQLException
337   {
338      mInnerConn.clearWarnings();
339   }
340
341   @Override
342   public Statement createStatement(int resultSetType, int resultSetConcurrency)
343         throws SQLException
344   {
345      return mInnerConn.createStatement(resultSetType, resultSetConcurrency);
346   }
347
348   @Override
349   public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
350         throws SQLException
351   {
352      return mInnerConn.prepareStatement(sql, resultSetType, resultSetConcurrency);
353   }
354
355   @Override
356   public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
357         throws SQLException
358   {
359      return mInnerConn.prepareCall(sql, resultSetType, resultSetConcurrency);
360   }
361
362   @Override
363   public Map<String, Class<?>> getTypeMap()
364         throws SQLException
365   {
366      return mInnerConn.getTypeMap();
367   }
368
369   @Override
370   public void setTypeMap(Map<String, Class<?>> map)
371         throws SQLException
372   {
373      mInnerConn.setTypeMap(map);
374   }
375
376   @Override
377   public void setHoldability(int inValue)
378         throws SQLException
379   {
380      mInnerConn.setHoldability(inValue);
381   }
382
383   @Override
384   public int getHoldability()
385         throws SQLException
386   {
387      return mInnerConn.getHoldability();
388   }
389
390   @Override
391   public Savepoint setSavepoint()
392         throws SQLException
393   {
394      return mInnerConn.setSavepoint();
395   }
396
397   @Override
398   public Savepoint setSavepoint(String name)
399         throws SQLException
400   {
401      return mInnerConn.setSavepoint(name);
402   }
403
404   @Override
405   public void rollback(Savepoint savepoint)
406         throws SQLException
407   {
408      mInnerConn.rollback(savepoint);
409   }
410
411   @Override
412   public void releaseSavepoint(Savepoint savepoint)
413         throws SQLException
414   {
415      mInnerConn.releaseSavepoint(savepoint);
416   }
417
418   @Override
419   public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
420         throws SQLException
421   {
422      return mInnerConn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
423   }
424
425   @Override
426   public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
427         throws SQLException
428   {
429      return mInnerConn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
430   }
431
432   @Override
433   public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
434         throws SQLException
435   {
436      return mInnerConn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
437   }
438
439   @Override
440   public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
441         throws SQLException
442   {
443      return mInnerConn.prepareStatement(sql, autoGeneratedKeys);
444   }
445
446   @Override
447   public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
448         throws SQLException
449   {
450      return mInnerConn.prepareStatement(sql, columnIndexes);
451   }
452
453   @Override
454   public PreparedStatement prepareStatement(String sql, String[] columnNames)
455         throws SQLException
456   {
457      return mInnerConn.prepareStatement(sql, columnNames);
458   }
459
460   @Override
461   public Clob createClob()
462         throws SQLException
463   {
464      return mInnerConn.createClob();
465   }
466
467   @Override
468   public Blob createBlob()
469         throws SQLException
470   {
471      return mInnerConn.createBlob();
472   }
473
474   @Override
475   public NClob createNClob()
476         throws SQLException
477   {
478      return mInnerConn.createNClob();
479   }
480
481   @Override
482   public SQLXML createSQLXML()
483         throws SQLException
484   {
485      return mInnerConn.createSQLXML();
486   }
487
488   @Override
489   public boolean isValid(int timeout)
490         throws SQLException
491   {
492      return mInnerConn.isValid(timeout);
493   }
494
495   @Override
496   public void setClientInfo(String name, String value)
497         throws SQLClientInfoException
498   {
499      mInnerConn.setClientInfo(name, value);
500   }
501
502   @Override
503   public void setClientInfo(Properties properties)
504         throws SQLClientInfoException
505   {
506      mInnerConn.setClientInfo(properties);
507   }
508
509   @Override
510   public String getClientInfo(String name)
511         throws SQLException
512   {
513      return mInnerConn.getClientInfo(name);
514   }
515
516   @Override
517   public Properties getClientInfo()
518         throws SQLException
519   {
520      return mInnerConn.getClientInfo();
521   }
522
523   @Override
524   public Array createArrayOf(String typeName, Object[] elements)
525         throws SQLException
526   {
527      return mInnerConn.createArrayOf(typeName, elements);
528   }
529
530   @Override
531   public Struct createStruct(String typeName, Object[] attributes)
532         throws SQLException
533   {
534      return mInnerConn.createStruct(typeName, attributes);
535   }
536
537   @Override
538   public void setSchema(String schema)
539         throws SQLException
540   {
541      mInnerConn.setSchema(schema);
542   }
543
544   @Override
545   public String getSchema()
546         throws SQLException
547   {
548      return mInnerConn.getSchema();
549   }
550
551   @Override
552   public void abort(Executor executor)
553         throws SQLException
554   {
555      mInnerConn.abort(executor);
556   }
557
558   @Override
559   public void setNetworkTimeout(Executor executor, int milliseconds)
560         throws SQLException
561   {
562      mInnerConn.setNetworkTimeout(executor, milliseconds);
563   }
564
565   @Override
566   public int getNetworkTimeout()
567         throws SQLException
568   {
569      return mInnerConn.getNetworkTimeout();
570   }
571
572   @Override
573   public <T> T unwrap(Class<T> iface)
574         throws SQLException
575   {
576      return mInnerConn.unwrap(iface);
577   }
578
579   @Override
580   public boolean isWrapperFor(Class<?> iface)
581         throws SQLException
582   {
583      return mInnerConn.isWrapperFor(iface);
584   }
585
586   //###########################################################################
587   // PROTECTED METHODS
588   //###########################################################################
589
590   //---------------------------------------------------------------------------
591   protected Connection getInnerConnection()
592   {
593      return mInnerConn;
594   }
595
596   //###########################################################################
597   // PRIVATE METHODS
598   //###########################################################################
599
600   //---------------------------------------------------------------------------
601   private Connection establishConnection()
602   {
603      return mServer.getConnection(mDatabaseName, mCredentials, mSettings);
604   }
605
606}