001/*
002 *  $Source: v:/cvsroot/open/projects/WebARTS/ca/bc/webarts/widgets/JDBCAdapter.java,v $
003 *  $Name:  $
004 *  $Revision: 567 $
005 *  $Date: 2012-11-03 20:36:02 -0700 (Sat, 03 Nov 2012) $
006 *  $Locker:  $
007 */
008/*
009 *  Copyright (C) 2001 WebARTS Design, North Vancouver Canada
010 *  http://www..webarts.bc.ca
011 *
012 *  This program is free software; you can redistribute it and/or modify
013 *  it under the terms of the GNU General Public License as published by
014 *  the Free Software Foundation; either version 2 of the License, or
015 *  (at your option) any later version.
016 *
017 *  This program is distributed in the hope that it will be useful,
018 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
019 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
020 *  GNU General Public License for more details.
021 *
022 *  You should have received a copy of the GNU General Public License
023 *  along with this program; if not, write to the Free Software
024 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
025 */
026package ca.bc.webarts.widgets;
027
028
029import java.sql.*;
030import java.util.Enumeration;
031
032import java.util.Vector;
033import javax.swing.event.TableModelEvent;
034import javax.swing.table.AbstractTableModel;
035
036/**
037 * JDBCAdapter is an encapsulation class, transforming the JDBC interface to the
038 * <code>TableModel</code> interface. This class attempts to abstract the details
039 * of the JDBC DB connection and query specifics. It presents all the results to
040 * the user in the form of a Java <code>TableModel</code> by extending/overiding
041 * the <code>AbstractTableModel</code>. This an then be used to as the basis for
042 * a JTable.<BR>
043 * <BR>
044 * <U>example usage</U> <BR>
045 * <code><PRE>
046 *      JDBCAdapter       jdbcTableAdapter;
047 *      private String    dbVendorParam = "oracle",           // this can be oracle or db2
048 *                        dbNameParam = "someDB",           // this is the name of the DB to hit
049 *                        dbUsernameParam = "myUsername",   // duh
050 *                        dbPasswordParam = "myPassword,   // duh
051 *                        dbServerParam = "172.118.88.2",  // the name or IP address of the server runing the DB
052 *                        dbPortParam = "1521";             // the port to use for JDBC access (oracle does not have a seperate listening port it uses the main port)
053 *                                                      // see the vendor docs for the specific port to access
054 *      boolean          dbConnected = false;
055 *      String           sampleQuery = "SELECT * from employee";
056 *      TableSorter      dataSorter     = new TableSorter();
057 *      Table            table;
058 *
059 *      try {
060 *          jdbcTableAdapter = new JDBCAdapter(dbVendorParam, dbNameParam, dbUsernameParam, dbPasswordParam, dbServerParam, dbPortParam);
061 *          dbConnected = true;
062 *      }
063 *      catch (java.security.AccessControlException ex) {
064 *             System.out.println("Security Exception Connecting to " + dbVendorParam +"'s Database." );
065 *         dbConnected = false;
066 *      }
067 *            // retrieve data from the database
068 *           if (jdbcTableAdapter.isDbConnected()) {
069 *              System.out.println("Retrieve Raw data from the database");
070 *                      jdbcTableAdapter.executeQuery(sampleQuery);
071 *
072 *            // now get the table set up.
073 *                      dataSorter.setModel(jdbcTableAdapter);
074 *          table = new JTable(dataSorter);
075 *           }
076 *</PRE> </code>
077 *
078 * @author    Philip Milne
079 * @author    Tom Gutwin
080 * @created   October 23, 2001
081 * @version   1.30 09/25/97
082 * @version   1.90 09/06/00
083 * @see       javax.swing.table.AbstractTableModel
084 */
085public class JDBCAdapter extends AbstractTableModel
086{
087  /**
088   * The JDBC Connection used for queries.
089   */
090  private Connection connection;
091  /**
092   * Description of the Field
093   */
094  private Statement statement;
095  /**
096   * Description of the Field
097   */
098  private String driverName;
099  /**
100   * Description of the Field
101   */
102  private String JDBCconnectString;
103  /**
104   * Description of the Field
105   */
106  private String JDBCconnectString2;
107  /**
108   * Description of the Field
109   */
110  private String dbConnectString;
111  /**
112   * Description of the Field
113   */
114  private String oracleDriverName = "oracle.jdbc.driver.OracleDriver";
115  /**
116   * Description of the Field
117   */
118  private String db2DriverName = "COM.ibm.db2.jdbc.net.DB2Driver";
119  /**
120   * Description of the Field
121   */
122  private ResultSet resultSet;
123  /**
124   * Description of the Field
125   */
126  private String[] columnNames = {};
127  /**
128   * Description of the Field
129   */
130  private Vector rows = new Vector();
131  /**
132   * Description of the Field
133   */
134  private ResultSetMetaData metaData;
135  /**
136   * Description of the Field
137   */
138  private String dbVendor;
139  /**
140   * Description of the Field
141   */
142  private String dbName;
143  /**
144   * Description of the Field
145   */
146  private String dbUsername;
147  /**
148   * Description of the Field
149   */
150  private String dbPassword;
151  /**
152   * Description of the Field
153   */
154  private String dbServer;
155  /**
156   * Description of the Field
157   */
158  private String dbPort;
159  /**
160   * Description of the Field
161   */
162  private boolean errorCondition = false;
163
164
165  /**
166   * Main Constructor for the class. It takes all the parameters to get the connection
167   * set up. It also prints error messages for problems that might be encountered
168   * when getting set up. To do the actaual query call the executeQuery method.
169   *
170   * @param dbV     DB Vendor
171   * @param dbN     DB Name
172   * @param dbU     Username
173   * @param dbPass  Password
174   * @param dbS     The DB Server Name.
175   * @param dbP     The JDBC Port on the DB Server
176   */
177  public JDBCAdapter(String dbV, String dbN, String dbU, String dbPass, String dbS, String dbP)
178  {
179
180    dbVendor = dbV;
181    dbName = dbN;
182    dbUsername = dbU;
183    dbPassword = dbPass;
184    dbServer = dbS;
185    dbPort = dbP;
186
187    if (dbVendor.equals("oracle"))
188    {
189      driverName = oracleDriverName;
190      try
191      {
192        System.out.println("Registering the " + dbVendor + " JDBC Driver.");
193        //DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
194        Class.forName(driverName);
195
196        /*
197         *  Form the database connect string(TNSNAMES entry) as a name-value pair
198         */
199        dbConnectString = dbServer + ":" + dbPort + ":" + dbName;
200        JDBCconnectString = new String("jdbc:" + dbVendor + ":thin:@" +
201                                       dbConnectString);
202        JDBCconnectString2 = new String("jdbc:oracle:thin:" + dbUsername +
203                                      "/" + dbPassword + "@" + dbConnectString);
204
205        System.out.println("Opening db connection: " + JDBCconnectString);
206
207        connection = DriverManager.getConnection(JDBCconnectString, dbUsername, dbPassword);
208        statement = connection.createStatement();
209      }
210      catch (ClassNotFoundException ex)
211      {
212        System.err.println("Cannot find the database driver classes.");
213        System.err.println(ex);
214      }
215      catch (SQLException ex)
216      {
217        System.err.println("Cannot connect to this database.");
218        System.err.println(ex);
219        ex.printStackTrace();
220      }
221      /*
222       *  catch (java.security.AccessControlException ex) {
223       *  System.err.println("Cannot connect to this database-Security Permission Error");
224       *  System.err.println(ex);
225       *  }
226       */
227    }
228    else
229    {
230      if (dbVendor.equals("db2"))
231      {
232        driverName = db2DriverName;
233        /*
234         *  Form the database connect string(TNSNAMES entry) as a name-value pair
235         */
236        dbConnectString = dbServer + ":" + dbPort + "/" + dbName;
237        JDBCconnectString = new String("jdbc:" + dbVendor + "://" + dbConnectString);
238        try
239        {
240          System.out.println("Registering the " + dbVendor + " JDBC Driver.");
241          Class.forName(driverName);
242          System.out.println("Opening db connection: " + JDBCconnectString);
243          System.out.println("With User: " + dbUsername + " : " + dbPassword);
244
245          connection = DriverManager.getConnection(JDBCconnectString, dbUsername, dbPassword);
246          statement = connection.createStatement();
247        }
248        catch (ClassNotFoundException ex)
249        {
250          System.err.println("Cannot find the database driver classes.");
251          System.err.println(ex);
252        }
253        catch (SQLException ex)
254        {
255          System.err.println("Cannot connect to this database.");
256          System.err.println(ex);
257        }
258        /*
259         *  catch (java.security.AccessControlException ex) {
260         *  System.err.println("Cannot connect to this database-Security Permission Error");
261         *  System.err.println(ex);
262         *  }
263         */
264      }
265      else
266      {
267        System.out.println("Sorry Unsupported DBVendor <Supports - Oracle or IBM DB2> ");
268      }
269    }
270
271  }
272
273
274  /**
275   * Copy constructor for this class. It takes an existing JDBCAdapter and returns
276   * a new instantiated JDBCAdapter
277   *
278   * @param jdbcAdapter  Description of Parameter
279   */
280  public JDBCAdapter(JDBCAdapter jdbcAdapter)
281  {
282    dbVendor = jdbcAdapter.getdbVendor();
283    dbName = jdbcAdapter.getdbName();
284    dbUsername = jdbcAdapter.getdbUsername();
285    dbPassword = jdbcAdapter.getdbPassword();
286    dbServer = jdbcAdapter.getdbServer();
287    dbPort = jdbcAdapter.getdbPort();
288
289    if (dbVendor.equals("oracle"))
290    {
291      driverName = oracleDriverName;
292      try
293      {
294        System.out.println("Registering the " + dbVendor + " JDBC Driver.");
295        //DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
296        Class.forName(driverName);
297
298        /*
299         *  Form the database connect string(TNSNAMES entry) as a name-value pair
300         */
301        dbConnectString = dbServer + ":" + dbPort + ":" + dbName;
302        JDBCconnectString = new String("jdbc:" + dbVendor + ":thin:@" + dbConnectString);
303        JDBCconnectString2 = new String("jdbc:oracle:thin:" + dbUsername + "/" + dbPassword + "@" + dbConnectString);
304
305        System.out.println("Opening db connection: " + JDBCconnectString);
306
307        connection = DriverManager.getConnection(JDBCconnectString, dbUsername, dbPassword);
308        statement = connection.createStatement();
309      }
310      catch (ClassNotFoundException ex)
311      {
312        System.err.println("Cannot find the database driver classes.");
313        System.err.println(ex);
314      }
315      catch (SQLException ex)
316      {
317        System.err.println("Cannot connect to this database.");
318        System.err.println(ex);
319        ex.printStackTrace();
320      }
321      /*
322       *  catch (java.security.AccessControlException ex) {
323       *  System.err.println("Cannot connect to this database-Security Permission Error");
324       *  System.err.println(ex);
325       *  }
326       */
327    }
328    else
329    {
330      if (dbVendor.equals("db2"))
331      {
332        driverName = db2DriverName;
333        /*
334         *  Form the database connect string(TNSNAMES entry) as a name-value pair
335         */
336        dbConnectString = dbServer + ":" + dbPort + "/" + dbName;
337        JDBCconnectString = new String("jdbc:" + dbVendor + "://" + dbConnectString);
338        try
339        {
340          System.out.println("Registering the " + dbVendor + " JDBC Driver.");
341          Class.forName(driverName);
342          System.out.println("Opening db connection: " + JDBCconnectString);
343          System.out.println("With User: " + dbUsername + " : " + dbPassword);
344
345          connection = DriverManager.getConnection(JDBCconnectString, dbUsername, dbPassword);
346          statement = connection.createStatement();
347        }
348        catch (ClassNotFoundException ex)
349        {
350          System.err.println("Cannot find the database driver classes.");
351          System.err.println(ex);
352        }
353        catch (SQLException ex)
354        {
355          System.err.println("Cannot connect to this database.");
356          System.err.println(ex);
357        }
358        /*
359         *  catch (java.security.AccessControlException ex) {
360         *  System.err.println("Cannot connect to this database-Security Permission Error");
361         *  System.err.println(ex);
362         *  }
363         */
364      }
365      else
366      {
367        System.out.println("Sorry Unsupported DBVendor <Supports - Oracle or IBM DB2> ");
368      }
369    }
370  }
371
372
373  /**
374   * This constructor takes a URL (in the form of a String) as well as the Drivername,
375   * username, password and then creates the Adapter. Thgis allows the constructor
376   * to set up the connection without having to pass the vendor/DB/server info
377   * seperately.
378   *
379   * @param url         Description of Parameter
380   * @param driverName  Description of Parameter
381   * @param user        Description of Parameter
382   * @param passwd      Description of Parameter
383   */
384  public JDBCAdapter(String url, String driverName,
385    String user, String passwd)
386  {
387    try
388    {
389      Class.forName(driverName);
390      System.out.println("Opening db connection");
391
392      connection = DriverManager.getConnection(url, user, passwd);
393      statement = connection.createStatement();
394    }
395    catch (ClassNotFoundException ex)
396    {
397      System.err.println("Cannot find the database driver classes.");
398      System.err.println(ex);
399    }
400    catch (SQLException ex)
401    {
402      System.err.println("Cannot connect to this database.");
403      System.err.println(ex);
404    }
405  }
406
407
408  /**
409   * Sets the ErrorCondition attribute of the JDBCAdapter object
410   *
411   * @param val  The new ErrorCondition value
412   */
413  public void setErrorCondition(boolean val)
414  {
415    errorCondition = val;
416  }
417
418
419  /**
420   * Sets the value at the specified cell in the DB to the object supplied. It
421   * will take care of any casting/ type changing required to get it into the speced
422   * column. this method is for the Implementation of the TableModel Interface
423   *
424   * @param value   The new ValueAt value
425   * @param row     The new ValueAt value
426   * @param column  The new ValueAt value
427   */
428  public void setValueAt(Object value, int row, int column)
429  {
430    try
431    {
432      String tableName = metaData.getTableName(column + 1);
433      // Some of the drivers seem buggy, tableName should not be null.
434      if (tableName == null)
435      {
436        System.out.println("Table name returned null.");
437      }
438      String columnName = getColumnName(column);
439      String query =
440        "update " + tableName +
441        " set " + columnName + " = " + dbRepresentation(column, value) +
442        " where ";
443      // We don't have a model of the schema so we don't know the
444      // primary keys or which columns to lock on. To demonstrate
445      // that editing is possible, we'll just lock on everything.
446      for (int col = 0; col < getColumnCount(); col++)
447      {
448        String colName = getColumnName(col);
449        if (colName.equals(""))
450        {
451          continue;
452        }
453        if (col != 0)
454        {
455          query = query + " and ";
456        }
457        query = query + colName + " = " +
458          dbRepresentation(col, getValueAt(row, col));
459      }
460      System.out.println(query);
461      System.out.println("Not sending update to database");
462      // statement.executeQuery(query);
463    }
464    catch (SQLException e)
465    {
466      //     e.printStackTrace();
467      System.err.println("Update failed");
468    }
469    Vector dataRow = (Vector) rows.elementAt(row);
470    dataRow.setElementAt(value, column);
471
472  }
473
474
475  /**
476   * Gets the ErrorCondition attribute of the JDBCAdapter object
477   *
478   * @return   The ErrorCondition value
479   */
480  public boolean getErrorCondition()
481  {
482    return errorCondition;
483  }
484
485
486  /**
487   * Tells whether there is an active and usable DB connection
488   *
489   * @return   boolean flags if there is an active and usable DB connection
490   */
491  public boolean isDbConnected()
492  {
493    boolean retVal = false;
494    if (connection != null)
495    {
496      retVal = true;
497    }
498    return retVal;
499  }
500
501
502  //////////////////////////////////////////////////////////////////////////
503  //
504  //             Implementation of the TableModel Interface
505  //
506  //////////////////////////////////////////////////////////////////////////
507
508  // MetaData
509
510  /**
511   * Returns a String holding the name of the column name for the column number
512   * that was passed in as a parameter this method is for the Implementation of
513   * the TableModel Interface
514   *
515   * @param column  Description of Parameter
516   * @return        String the name for the indicated column
517   */
518  public String getColumnName(int column)
519  {
520//        System.err.println("getColumnName for -->" + (new Integer(column)).toString() + " is --->" + columnNames[column]);
521    if (columnNames[column] != null)
522    {
523      return columnNames[column];
524    }
525    else
526    {
527      return "";
528    }
529  }
530
531
532  /**
533   * This method returns the Java Class type held in the specified column. it will
534   * be String.class, Boolean.class, Integer.class etc. this method is for the
535   * Implementation of the TableModel Interface
536   *
537   * @param column  Description of Parameter
538   * @return        Class the Java Class representation of the object held in the
539   *      specified column.
540   */
541  public Class getColumnClass(int column)
542  {
543    int type;
544    try
545    {
546      type = metaData.getColumnType(column + 1);
547    }
548    catch (SQLException e)
549    {
550      return super.getColumnClass(column);
551    }
552
553    switch (type)
554    {
555      case Types.CHAR:
556      case Types.VARCHAR:
557      case Types.LONGVARCHAR:
558        return String.class;
559      case Types.BIT:
560        return Boolean.class;
561      case Types.TINYINT:
562      case Types.SMALLINT:
563      case Types.INTEGER:
564        return Integer.class;
565      case Types.BIGINT:
566        return Long.class;
567      case Types.FLOAT:
568      case Types.DOUBLE:
569        return Double.class;
570      case Types.DATE:
571        return java.sql.Date.class;
572      default:
573        return Object.class;
574    }
575  }
576
577
578  /**
579   * This tells you if the specified cell is editable(ie writeable). this method
580   * is for the Implementation of the TableModel Interface
581   *
582   * @param row     Description of Parameter
583   * @param column  Description of Parameter
584   * @return        The CellEditable value
585   */
586  public boolean isCellEditable(int row, int column)
587  {
588    try
589    {
590      return metaData.isWritable(column + 1);
591    }
592    catch (SQLException e)
593    {
594      return false;
595    }
596  }
597
598
599  /**
600   * Provides and accessor for the number of columns in the table model. this method
601   * is for the Implementation of the TableModel Interface
602   *
603   * @return   int the number of columns in the table model
604   */
605  public int getColumnCount()
606  {
607    return columnNames.length;
608  }
609
610  // Data methods
611
612  /**
613   * Provides and accessor for the number of rows in the table model. this method
614   * is for the Implementation of the TableModel Interface
615   *
616   * @return   int the number of rows in the table model
617   */
618  public int getRowCount()
619  {
620    return rows.size();
621  }
622
623
624  /**
625   * Provides and accessor for the object at a specific cell in the table model.
626   * this method is for the Implementation of the TableModel Interface
627   *
628   * @param aRow     Description of Parameter
629   * @param aColumn  Description of Parameter
630   * @return         Object the Object at the specified cell in the table model
631   */
632  public Object getValueAt(int aRow, int aColumn)
633  {
634    Vector row = (Vector) rows.elementAt(aRow);
635    return row.elementAt(aColumn);
636  }
637
638
639  /**
640   * This method takes a String and executes as an SQL statement on the connected
641   * Database. It enters all the results as rows in the table model that this class
642   * represents
643   *
644   * @param query   Description of Parameter
645   * @return        boolean flag to specify if the query was successful
646   */
647  public boolean executeQuery(String query)
648  {
649    boolean retVal = true;
650    if (connection == null)
651    {
652      System.err.println("There is no database to execute the query.");
653      return false;
654    }
655    if (statement == null)
656    {
657      System.err.println("The query string that was passed to the JDBCAdapter was null.");
658      return false;
659    }
660    if (getErrorCondition())
661    {
662      return false;
663    }
664
665    try
666    {
667      resultSet = statement.executeQuery(query);
668      metaData = resultSet.getMetaData();
669
670      int numberOfColumns = metaData.getColumnCount();
671      columnNames = new String[numberOfColumns];
672      // Get the column names and cache them.
673      // Then we can close the connection.
674//            System.err.println("!!Debug!! Columns Retrieved");
675      for (int column = 0; column < numberOfColumns; column++)
676      {
677        columnNames[column] = metaData.getColumnLabel(column + 1);
678//              System.err.println(columnNames[column]);
679      }
680
681      // Get all rows.
682      rows = new Vector();
683      while (resultSet.next())
684      {
685        Vector newRow = new Vector();
686        for (int i = 1; i <= getColumnCount(); i++)
687        {
688          newRow.addElement(resultSet.getObject(i));
689        }
690        rows.addElement(newRow);
691      }
692      //  close(); Need to copy the metaData, bug in jdbc:odbc driver.
693      fireTableChanged(null); // Tell the listeners a new table has arrived.
694    }
695    catch (SQLException ex)
696    {
697      retVal = false;
698      setErrorCondition(true);
699      System.err.println("JDBCAdapter: SQL Error");
700      System.err.println(ex);
701    }
702    return retVal;
703  }
704
705
706  /**
707   * This method performs some clean up for the class. If there is still JDBC objects
708   * open... it closes them so we can efinish up with this class cleanly.
709   */
710  public void close()
711  {
712    System.out.println("Closing db connection");
713    try
714    {
715      if (resultSet != null)
716      {
717        resultSet.close();
718      }
719      if (statement != null)
720      {
721        statement.close();
722      }
723      if (connection != null)
724      {
725        connection.close();
726      }
727    }
728    catch (Exception ex)
729    {
730      System.out.println("Can't Close db Connection exception caught");
731    }
732  }
733
734
735  /**
736   * Provides a method to get the Object data into a String form so it can be put
737   * into the DB. It returns the specified Object in the form of a String.
738   *
739   * @param column  Description of Parameter
740   * @param value   Description of Parameter
741   * @return        string the String representation of the data Object you supplied
742   */
743  public String dbRepresentation(int column, Object value)
744  {
745    int type;
746
747    if (value == null)
748    {
749      return "null";
750    }
751
752    try
753    {
754      type = metaData.getColumnType(column + 1);
755    }
756    catch (SQLException e)
757    {
758      return value.toString();
759    }
760
761    switch (type)
762    {
763      case Types.INTEGER:
764      case Types.DOUBLE:
765      case Types.FLOAT:
766        return value.toString();
767      case Types.BIT:
768        return ((Boolean) value).booleanValue() ? "1" : "0";
769      case Types.DATE:
770        return value.toString(); // This will need some conversion.
771      default:
772        return "\"" + value.toString() + "\"";
773    }
774
775  }
776
777
778  /**
779   * Get method for this field
780   *
781   * @return   The Rows value
782   */
783  protected Vector getRows()
784  {
785    return rows;
786  }
787
788
789  /**
790   * This method is here to ensure that we exit cleanly from the JDBC connections
791   * that might have been made. It simply calls the close() method.
792   *
793   * @exception Throwable  Description of Exception
794   */
795  protected void finalize() throws Throwable
796  {
797    close();
798    //   super.finalize();
799  }
800
801
802  /**
803   * Get method for this field
804   *
805   * @return   Description of the Returned Value
806   */
807  private String getdbVendor()
808  {
809    return dbVendor;
810  }
811
812
813  /**
814   * Get method for this field
815   *
816   * @return   Description of the Returned Value
817   */
818  private String getdbName()
819  {
820    return dbName;
821  }
822
823
824  /**
825   * Get method for this field
826   *
827   * @return   Description of the Returned Value
828   */
829  private String getdbUsername()
830  {
831    return dbUsername;
832  }
833
834
835  /**
836   * Get method for this field
837   *
838   * @return   Description of the Returned Value
839   */
840  private String getdbPassword()
841  {
842    return dbPassword;
843  }
844
845
846  /**
847   * Get method for this field
848   *
849   * @return   Description of the Returned Value
850   */
851  private String getdbServer()
852  {
853    return dbServer;
854  }
855
856
857  /**
858   * Get method for this field
859   *
860   * @return   Description of the Returned Value
861   */
862  private String getdbPort()
863  {
864    return dbPort;
865  }
866}
867
868