home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / VCafe / prosrc.bin / JdbcConnection.java < prev    next >
Encoding:
Java Source  |  1998-03-18  |  41.9 KB  |  1,222 lines

  1.  
  2. /*
  3.  * @(#)JdbcConnection.java
  4.  *
  5.  * Copyright (c) 1997 Symantec Corporation. All Rights Reserved.
  6.  *
  7.  */
  8.  
  9. package symantec.itools.db.beans.jdbc;
  10.  
  11. import java.io.* ;
  12. import java.sql.* ;
  13. import java.beans.*;
  14. import java.lang.*;
  15. import java.util.Properties ;
  16. import java.util.Vector;
  17. import java.util.Stack;
  18.  
  19. /**
  20.  * <P>The class that wraps a JDBC Connection object.
  21.  * Provides properties to establish a database connection as well as properties to
  22.  * modify the establish connection.
  23.  *
  24.  * Provides its own versioning logic.
  25.  *
  26.  *
  27.  *
  28.  * Is Event source for ConnectionEvents OPEN and CLOSE. Note the signature of closing
  29.  * a connection is setConnectionClosed (boolean) throws PropertyVetoException.
  30.  * This signature conforms with JavaBeans Design Pattern for Constrained Properties.
  31.  * Other beans can control whether a Connection should close by registering a
  32.  * VetoableChangeListener and throwing a PropertyVetoException when notified.
  33.  *
  34.  * Implements doAutoStart() by establishing a database connection during object
  35.  * de-serialization.
  36.  *
  37.  * @see JDBCBean
  38.  * @see java.sql.Connection
  39.  *
  40.  */
  41.  
  42. public class JdbcConnection extends JDBCBean
  43.     implements symantec.itools.db.beans.binding.Connection
  44. {
  45.  
  46. //PUBLIC SECTION
  47.  
  48.  
  49. //Constructors
  50.     /**
  51.      * Default constructor. Bean-aware IDE's and serialization require all beans
  52.      * have a default constructor.
  53.      */
  54.     public JdbcConnection () {
  55.         }
  56.  
  57.     /**
  58.     * Constructor. Beans should provide a constructor with commonly-used properties
  59.     * as parameters.  This allows the beans to be constructed programatically without
  60.     * having to call the properties' setter methods.
  61.     *
  62.     * @see Connection#connectFailed
  63.     * @see Connection#connect
  64.     *
  65.     * @param       driverName.     The fully qualified name of the JDBC Driver class.
  66.     * @param       url.            The JDBC compliant url that will be used to connect to a database.
  67.     * @param       username.       The name used to log onto the database.
  68.     * @param       password.       The password for the user.
  69.     */
  70.     public JdbcConnection (String driverName, String URL,
  71.                        String userName, String password) {
  72.         this();
  73.         setDriverName(driverName);
  74.         setURL(url);
  75.         setUserName(userName);
  76.         setPassword(password);
  77.     }
  78.  
  79. //Methods
  80.     /**
  81.      * Connects to the database referenced within the url property.
  82.      *
  83.      * If the driverName property is provided, the driver is loaded prior to obtaining a
  84.      * connection from the DriverManager. If the driverName is empty, the user is responsible
  85.      * for loading the driver prior to calling connect.
  86.      *
  87.      * The getConnectionProperties() method is called to build a Driver-specific Properties
  88.      * object. This method returns a Properties object with the userName and password properties
  89.      * set. Override this method to return additional properties that driverName expects.
  90.      *
  91.      * The connectFailed method will be called if an SQLException was thrown attempting to connect.
  92.      * Descendents of Connection can override this method to obtain a username, password, or url
  93.      * from the user via a dialog. After the database connection is established, the 'runtime' properties
  94.      * are applied to the database -- catalog, autocommit, transactionIsolation and readonly.
  95.      *
  96.      * @see     java.sql.DriverManager#getConnection
  97.      * @see     java.sql.Connection
  98.      * @see     Connection#connectFailed
  99.      * @see     Connection#getConnectionProps
  100.      *
  101.      * @exception   SQLException
  102.      */
  103.     public synchronized void connect () throws SQLException
  104.     {
  105.         if (!isConnectionClosed()) {
  106.             return; //allow multiple calls to connect.
  107.         }
  108.         String javaVendor = System.getProperty("java.vendor");
  109.         boolean javaVendorIsMicrosoft = javaVendor.startsWith("Microsoft");
  110.         String javaVersion = System.getProperty("java.version");
  111.  
  112.  
  113.         // Don't forget that a length of zero is OK
  114.         // as long as driver is registered by some other means.
  115.         if (getDriverName().length() != 0) {
  116.             //load the driver so it can register itself
  117.             try {
  118.                 Driver drv=(Driver)Class.forName(getDriverName()).newInstance();
  119.             }
  120.             catch (ClassNotFoundException e) {
  121.                 String reason = "Could not register JDBC driver.  Driver class name: " + e.getMessage();
  122.                 System.out.println(reason);
  123.                 throw new SQLException(reason);
  124.             }
  125.             catch (Exception e) {
  126.                 String reason = "Could not register JDBC driver.  Driver class name: " + e.getMessage();
  127.                 System.out.println(reason);
  128.                 throw new SQLException(reason);
  129.             }
  130.         }
  131.  
  132.         try {
  133.                     // If Microsoft IE in Windows, then turn on system permissions
  134.                     // to allow debugging from VC dbDE into IE.  This is needed for
  135.                     // creation of sockets to localhost, IP address, etc.
  136.  
  137.             if (symantec.itools.lang.OS.isWindows() && javaVendorIsMicrosoft)
  138.             {
  139.                 Class msSecurityAccess=Class.forName("symantec.itools.db.beans.jdbc.MSSecurityAccess");
  140.                 Class classParam[]=new Class[3];
  141.                 classParam[0]=Class.forName("java.lang.String");
  142.                 classParam[1]=Class.forName("java.lang.String");
  143.                 classParam[2]=Class.forName("java.lang.String");
  144.                 java.lang.reflect.Method getConnection=msSecurityAccess.getMethod("getConnection",classParam);
  145.                 String param[]={getURL(),getUserName(),getPassword()};
  146.                 lowLevelConnection=(Connection)getConnection.invoke(null,param);
  147.  
  148.             }
  149.             else{
  150.                 lowLevelConnection = DriverManager.getConnection(getURL(),getConnectionProps());
  151.             }
  152.         }
  153.         catch (SQLException ex) {
  154.             System.out.println(ex.getMessage());
  155.             connectFailed(new ConnectFailedEvent(this, ConnectFailedEvent.GENERALERROR, ex.getMessage()));
  156.             if (isConnectionClosed()) {
  157.                 throw new SQLException(ex.getMessage());
  158.             }
  159.             else {
  160.                 return;
  161.             }
  162.         }
  163.          catch(Exception e){
  164.                 System.out.println(e.getMessage());
  165.          }
  166.  
  167.         if (lowLevelConnection != null) {
  168.             try {
  169.                 DatabaseMetaData md = lowLevelConnection.getMetaData();
  170.                 defaultTransactionIsolation = md.getDefaultTransactionIsolation();
  171.             }
  172.             catch (SQLException ex) {
  173.                 System.out.println("Warning: could not determine default transaction isolation level: " + ex.getMessage());
  174.                 System.out.println("Defaulting to TRANSACTION_NONE");
  175.                 defaultTransactionIsolation = TRANSACTION_NONE;
  176.             }
  177.  
  178.             //set connection properties
  179.             try {
  180.                 lowLevelConnection.setAutoCommit(autoCommit);
  181.             }
  182.             catch (SQLException ex) {
  183.                 System.out.println("Warning: could not apply autoCommit property to live connection: " + ex.getMessage());
  184.             }
  185.  
  186.             try {
  187.                 lowLevelConnection.setReadOnly(readOnly);
  188.             }
  189.             catch (SQLException ex) {
  190.                 System.out.println("Warning: could not apply readOnly property to live connection: " + ex.getMessage());
  191.             }
  192.  
  193.             try {
  194.                 if (transactionIsolation != TRANSACTION_DEFAULT) {
  195.                     lowLevelConnection.setTransactionIsolation(transactionIsolation);
  196.                 }
  197.             }
  198.             catch (SQLException ex) {
  199.                 System.out.println("Warning: could not apply transactionIsolation property to live connection: " + ex.getMessage());
  200.             }
  201.  
  202.             try {
  203.                 if (catalog.length() != 0 ) {
  204.                     lowLevelConnection.setCatalog(catalog);
  205.                 }
  206.             }
  207.             catch (SQLException ex) {
  208.                 System.out.println("Warning: could not apply catalog property to live connection: " + ex.getMessage());
  209.             }
  210.         }
  211.     }
  212.  
  213.     /**
  214.      * This method starts a transaction...
  215.      *    - verify the connection supports transactions
  216.      *    - push current state information on a stack
  217.      *    - close open statements
  218.      *    - Set auto-commit mode to false
  219.      *
  220.      * Note swallow any exceptions - if we can't begin a transaction
  221.      *  we silently continue in auto-commit mode.
  222.      *
  223.      * @see endTransaction
  224.      *
  225.      * @return      void
  226.      */
  227.     public synchronized void beginTransaction()
  228.     {
  229.         try {
  230.         if (!getMetaData().supportsTransactions()) {
  231.             return;
  232.         }
  233.  
  234.         boolean mode = isAutoCommit();
  235.  
  236.         m_AutoCommitMode.push(new Boolean(mode));
  237.         m_TransactionState.push(new Boolean(true));
  238.  
  239.         // foo : Since a save will now trigger a beginTransaction (master-detail)
  240.         // it becomes neccessary to restrict this to drivers like Access.
  241.         // What determines if a driver is like Access?  there are two methods:
  242.         // supportsOpenStatementsAcrossCommit and supportsOpenStatementsAcrossRollback.
  243.         // This implies we should only close statements in commit() and rollback().
  244.         // foo : We should really track all statements, not just the latest.
  245.         // foo : Is this correct or is it just an Access bug?
  246.  
  247.         java.sql.DatabaseMetaData md = getMetaData();
  248.  
  249.         boolean supportsAcrossCommit = md.supportsOpenStatementsAcrossCommit();
  250.         boolean supportsAcrossRollback = md.supportsOpenStatementsAcrossRollback();
  251.  
  252.         if (!supportsAcrossCommit || !supportsAcrossRollback) {
  253.             if (m_Statement != null) {
  254.                 m_Statement.close();
  255.             }
  256.         }
  257.  
  258.         setAutoCommit(false);
  259.         }
  260.         catch (Exception ex) {
  261.         }
  262.     }
  263.  
  264.     /**
  265.      * This method ends a transaction...
  266.      *    - verify the connection supports transactions
  267.      *    - commit or rollback the present transaction
  268.      *    - pop state information from a stack
  269.      *    - Set auto-commit mode to true
  270.      *
  271.      * Note swallow any exceptions - if we can't end a transaction
  272.      *  we silently continue in auto-commit mode.
  273.      *
  274.      * @see endTransaction
  275.      *
  276.      * @return      void
  277.      */
  278.     public synchronized void endTransaction(boolean commitTransaction)
  279.     {
  280.         try {
  281.         if (!getMetaData().supportsTransactions()) {
  282.             return;
  283.         }
  284.  
  285.         Boolean transactionState = (Boolean)m_TransactionState.pop();
  286.  
  287.         if (commitTransaction && transactionState.booleanValue()) {
  288.             commit();
  289.         }
  290.         else {
  291.             rollback();
  292.         }
  293.  
  294.         Boolean mode = (Boolean)m_AutoCommitMode.pop();
  295.  
  296.         if (mode.booleanValue()) {
  297.             setAutoCommit(true);
  298.         }
  299.         }
  300.         catch (Exception ex) {
  301.         }
  302.     }
  303.  
  304.     /**
  305.      * This method prepares a SQL statement
  306.      *
  307.      * @see createStatement
  308.      *
  309.      * @param       sql         database DML string.
  310.      *
  311.      * @return      PreparedStatement   JDBC object.
  312.      */
  313.     public PreparedStatement prepareStatement(String sql) throws SQLException
  314.     {
  315.         return lowLevelConnection.prepareStatement(sql);
  316.     }
  317.  
  318.     /**
  319.      * This method creates a SQL statement
  320.      *
  321.      * @see prepareStatement
  322.      *
  323.      * @param       none
  324.      *
  325.      * @return      Statement   JDBC object.
  326.      */
  327.     public Statement createStatement() throws SQLException
  328.     {
  329.         return lowLevelConnection.createStatement();
  330.     }
  331.  
  332.     /**
  333.      * This method executes a statement.
  334.      *  - Execute DML
  335.      *  - if error occurs push status onto current state stack
  336.      *
  337.      * @see createStatement
  338.      * @see prepareStatement
  339.      *
  340.      * @param       statement   JDBC object
  341.      * @param       sql         database DML string.
  342.      *
  343.      * @return      int     number of rows updated.
  344.      */
  345.     public int executeStatement(Statement statement, String sql) throws SQLException
  346.     {
  347.         int rUpdated=0;
  348.         try {
  349.             if (statement instanceof PreparedStatement){
  350.                 rUpdated=((PreparedStatement)statement).executeUpdate();
  351.             }
  352.             else if(statement instanceof Statement){
  353.                 rUpdated=statement.executeUpdate(sql);
  354.             }
  355.             return rUpdated;
  356.         }
  357.         catch (SQLException ex) {
  358.             if (!m_TransactionState.empty()) {
  359.                 Boolean state = (Boolean)m_TransactionState.peek();
  360.                 if (state.booleanValue()) {
  361.                     m_TransactionState.pop();
  362.                     m_TransactionState.push(new Boolean(false));
  363.                 }
  364.             }
  365.             throw ex;
  366.         }
  367.     }
  368.  
  369.  
  370.     /**
  371.      * This method executes a query.
  372.      *  - Execute DML
  373.      *  - if error occurs push status onto current state stack
  374.      *
  375.      * @see createStatement
  376.      * @see prepareStatement
  377.      *
  378.      * @param       statement   JDBC object
  379.      * @param       sql         database query string.
  380.      *
  381.      * @return      ResultSet   result of query.
  382.      */
  383.     public ResultSet executeQuery(Statement statement, String sql) throws SQLException
  384.     {
  385.         m_Statement = statement;
  386.         ResultSet result = null;
  387.  
  388.         try {
  389.             if (statement instanceof PreparedStatement){
  390.                 result = ((PreparedStatement)statement).executeQuery();
  391.             }
  392.             else if(statement instanceof Statement){
  393.                 result = statement.executeQuery(sql);
  394.             }
  395.             return result;
  396.         }
  397.         catch (SQLException ex) {
  398.             if (!m_TransactionState.empty()) {
  399.                 Boolean state = (Boolean)m_TransactionState.peek();
  400.                 if (state.booleanValue()) {
  401.                     m_TransactionState.pop();
  402.                     m_TransactionState.push(new Boolean(false));
  403.                 }
  404.             }
  405.             throw ex;
  406.         }
  407.     }
  408.  
  409.     /**
  410.      * This method will be called whenever an attempt to connect to a database fails.
  411.      * The default behavior is to return false which will abort the connection. Descendents
  412.      * can override this method to provide specific connection failure handling by
  413.      * prompting the user for a new url, userName, or password. Set the properties with
  414.      * the appropriate setter method and return true. Returning false will
  415.      * abort the connection. An integer retries is provided to limit the number of
  416.      * connection attempts.
  417.      *
  418.      * @see Connection#connect
  419.      *
  420.      * @param       retries     The number of times a connection was attempted.
  421.      * @param       e           The SQLException thrown for the connect failure.
  422.      *
  423.      * @return      boolean     A false aborts the connection attempt. A true will initiate
  424.      *                          another connection attempt using the new userName, password,
  425.      *                          or url that may have been set within this method.
  426.      */
  427.     public void connectFailed (ConnectFailedEvent event)
  428.     {
  429.         try {
  430.             Class clazz = Class.forName(m_ConnectFailedListener);
  431.             ConnectFailedListener listener = (ConnectFailedListener)clazz.newInstance();
  432.             listener.connectFailed(event);
  433.         }
  434.         catch (Exception ex) {
  435.             // No point in telling we can't tell
  436.         }
  437.  
  438.         Vector listeners;
  439.         try {
  440.             synchronized(this) {
  441.                 listeners = (Vector)m_ConnectFailedListeners.clone();
  442.             }
  443.             for(int i = 0; i < listeners.size(); i++) {
  444.                ((ConnectFailedListener)listeners.elementAt(i)).connectFailed(event);
  445.             }
  446.         }
  447.         catch(Exception ex) {
  448.             // No point in telling we can't tell
  449.         }
  450.     }
  451.  
  452.     private String m_ConnectFailedListener = "symantec.itools.db.beans.jdbc.DefaultConnectFailedListener";
  453.     public void setConnectFailedListener(String listener)
  454.     {
  455.         m_ConnectFailedListener = listener;
  456.     }
  457.     public String getConnectFailedListener()
  458.     {
  459.         return m_ConnectFailedListener;
  460.     }
  461.  
  462.  
  463.     /**
  464.      * Commits all changes to the database.
  465.      *
  466.      * @see     java.sqlConnection#commit
  467.      *
  468.      * @exception   SQLException
  469.      */
  470.     public synchronized void commit() throws SQLException
  471.     {
  472.         try {
  473.             if (getSQLConnection() != null) {
  474.                 getSQLConnection().commit();
  475.             }
  476.             else {
  477.                 throw new SQLException("Connection is closed");
  478.             }
  479.         }
  480.         catch (SQLException e) {
  481.             handleException (e);
  482.         }
  483.  
  484.     }
  485.  
  486.     /**
  487.      * Rollback all changes to the database.
  488.      *
  489.      * @see     java.sql.Connection#rollback
  490.      *
  491.      * @exception   SQLException
  492.      */
  493.     public synchronized void rollback() throws SQLException
  494.     {
  495.         try {
  496.             if (getSQLConnection() != null) {
  497.                 getSQLConnection().rollback();
  498.             }
  499.             else {
  500.                 throw new SQLException("Connection is closed");
  501.             }
  502.         }
  503.         catch (SQLException e) {
  504.             handleException (e);
  505.         }
  506.     }
  507.  
  508.     /**
  509.      * Returns the metadata of the connection.
  510.      *
  511.      * @see     java.sql.Connection#getMetaData
  512.      * @see     java.sql.DatabaseMetaData
  513.      *
  514.      *
  515.      * @return      java.sql.DatabaseMetaData
  516.      * @exception   SQLException
  517.      */
  518.     public synchronized DatabaseMetaData getMetaData() throws SQLException
  519.     {
  520.         java.sql.DatabaseMetaData md = null ;
  521.         connect();
  522.         try {
  523.             if (getSQLConnection() != null) {
  524.                 md = getSQLConnection().getMetaData();
  525.             }
  526.             else {
  527.                 throw new SQLException("Connection is closed");
  528.             }
  529.         }
  530.         catch (SQLException e) {
  531.             handleException (e);
  532.         }
  533.  
  534.         return md ;
  535.  
  536.     }
  537.  
  538.  
  539. //Accessors/Modifiers
  540.  
  541.     /**
  542.      * Sets the identifier property. The identifier is an optional property of the Connection.
  543.      * If an identifier property is not defined, then the Connection
  544.      * will be identified by the standard combination of other properties.
  545.      *
  546.      * @param       identifier.     The string used to identify this connection.
  547.      *
  548.      */
  549.     public void setIdentifier (String value) {
  550.         identifier = value;
  551.     }
  552.  
  553.     /**
  554.      * Gets the identifier property.
  555.      *
  556.      * @return      The value of the identifier property.
  557.      */
  558.     public String getIdentifier () {
  559.         return identifier;
  560.     }
  561.  
  562.  
  563.     /**
  564.      * Sets the driverName property. The property is bound, so a PropertyChangeEvent will
  565.      * be sent to registered ProperyChange listeners. The driverName is an optional
  566.      * property of the Connection. If a driverName property is not defined, then it is
  567.      * up to the user of the bean to register the appropriate java.sql.Driver object
  568.      * with the DriverManager prior to calling connect.
  569.      *
  570.      * @see     Connection#connect
  571.      * @see     java.sql.DriverManager
  572.      * @see     java.sql.Driver
  573.      *
  574.      * @param       driverName.     The fully qualified classname of an object that has
  575.                                     implemented the java.sql.Driver interface.
  576.      *
  577.      */
  578.     public void setDriverName (String driverName)
  579.     {
  580.         if (java.beans.Beans.isDesignTime()) {
  581.             processPropertyChange(PROP_DRIVERNAME,this.driverName,driverName);
  582.         }
  583.         this.driverName = driverName ;
  584.     }
  585.  
  586.     /**
  587.      * Gets the driverName property.
  588.      *
  589.      * @return      The value of the driverName property.
  590.      */
  591.     public String getDriverName () {
  592.         return driverName ;
  593.     }
  594.  
  595.     /**
  596.      * Sets the url property. The property is bound, so a PropertyChangeEvent will
  597.      * be sent to registered ProperyChange listeners. The database url is in the
  598.      * form jdbc:subprotocol:subname.
  599.      *
  600.      * @see     Connection#connect
  601.      * @see     java.sql.DriverManager
  602.      * @see     java.sql.Driver
  603.      *
  604.      * @param       url     The databse url
  605.      *
  606.      */
  607.     public void setURL (String url)
  608.     {
  609.         if (java.beans.Beans.isDesignTime()) {
  610.             processPropertyChange(PROP_URL,this.url,url);
  611.         }
  612.         this.url = url;
  613.     }
  614.  
  615.     /**
  616.      * Gets the url property.
  617.      *
  618.      * @return      The value of the url property.
  619.      */
  620.     public String getURL () {
  621.         return url ;
  622.     }
  623.  
  624.     /**
  625.      * Sets the userName property. The property is bound, so a PropertyChangeEvent will
  626.      * be sent to registered ProperyChange listeners. The userName property is an optional
  627.      * property. If the value is not set, no user property will be added to connection
  628.      * properties in getConnectionProps. Note: an empty userName may result in a failure
  629.      * to connect. Override the connectFailed method to obtain a userName dynamically during
  630.      * the connection process.
  631.      *
  632.      * @see  Connection#connect
  633.      * @see  Connection#connectFailed
  634.      * @see  Connection#getConnectionProps
  635.      *
  636.      * @param       userName    The name of the user.
  637.      *
  638.      */
  639.     public void setUserName (String userName)
  640.     {
  641.         if (java.beans.Beans.isDesignTime()) {
  642.             processPropertyChange(PROP_USERNAME,this.userName,userName);
  643.         }
  644.         this.userName = userName ;
  645.     }
  646.  
  647.     /**
  648.      * Gets the userName property.
  649.      *
  650.      * @return      The value of the userName property.
  651.      */
  652.     public String getUserName () {
  653.         return userName;
  654.     }
  655.  
  656.     /**
  657.      * Sets the password property. The property is bound, so a PropertyChangeEvent will
  658.      * be sent to registered ProperyChange listeners. The password property is an optional
  659.      * property. If the value is not set, no password property will be added to connection
  660.      * properties in getConnectionProps. Note: an empty password may result in a failure
  661.      * to connect. Override the connectFailed method to obtain a password dynamically during
  662.      * the connection process.
  663.      *
  664.      * @see  Connection#connect
  665.      * @see  Connection#connectFailed
  666.      * @see  Connection#getConnectionProps
  667.      *
  668.      * @param       password    The password for the user.
  669.      */
  670.     public void setPassword (String password)
  671.     {
  672.         if (java.beans.Beans.isDesignTime()) {
  673.             processPropertyChange(PROP_PASSWORD,this.password,password);
  674.         }
  675.         this.password = password ;
  676.     }
  677.  
  678.     /**
  679.      * Gets the password property.
  680.      *
  681.      * @return      The value of the password property.
  682.      */
  683.     public String getPassword () {
  684.         return password;
  685.     }
  686.  
  687.     /**
  688.      * Sets the catalog property. The property is bound, so a PropertyChangeEvent will
  689.      * be sent to registered ProperyChange listeners. This property will be sent to
  690.      * the database if a connection is established.
  691.      *
  692.      * @see         java.sql.Connection#setCatalog
  693.      *
  694.      * @param       catalog     The catalog name.
  695.      *
  696.      * @exception   SQLException
  697.      */
  698.     public synchronized void setCatalog (String catalog) throws SQLException
  699.     {
  700.         if (java.beans.Beans.isDesignTime()) {
  701.             processPropertyChange(PROP_CATALOG,this.catalog,catalog);
  702.         }
  703.         this.catalog = catalog ;
  704.         try {
  705.             if (lowLevelConnection != null) {
  706.                 lowLevelConnection.setCatalog(catalog) ;
  707.             }
  708.         }
  709.         catch (SQLException e) {
  710.             handleException (e);
  711.         }
  712.     }
  713.  
  714.     /**
  715.      * Gets the catalog property. If a database connection is established, the catalog
  716.      * property will be set to the catalog returned from java.sql.Connection.getCatalog().
  717.      *
  718.      * @see     java.sql.Connection#getCatalog
  719.      *
  720.      * @return      the value of the catalog property.
  721.      * @exception   SQLException
  722.      */
  723.     public synchronized String getCatalog () throws SQLException
  724.     {
  725.         try {
  726.             if (lowLevelConnection != null) {
  727.                 catalog = lowLevelConnection.getCatalog();
  728.             }
  729.         }
  730.         catch (SQLException e) {
  731.             handleException (e);
  732.         }
  733.         return catalog;
  734.  
  735.     }
  736.  
  737.     /**
  738.      * Sets the autoCommit property. The property is bound, so a PropertyChangeEvent will
  739.      * be sent to registered ProperyChange listeners. This property will be sent to
  740.      * the database if a connection is established.
  741.      *
  742.      * @see         java.sql.Connection#setAutoCommit
  743.      *
  744.      * @param       autoCommit     The autocommit flag.
  745.      *
  746.      * @exception   SQLException
  747.      */
  748.     public synchronized void setAutoCommit (boolean autoCommit)
  749.         throws SQLException
  750.     {
  751.         if (java.beans.Beans.isDesignTime()) {
  752.             processPropertyChange(PROP_AUTOCOMMIT,
  753.                 new Boolean(this.autoCommit),new Boolean(autoCommit));
  754.         }
  755.         this.autoCommit = autoCommit ;
  756.         try {
  757.             if (lowLevelConnection != null) {
  758.                 lowLevelConnection.setAutoCommit(autoCommit) ;
  759.             }
  760.         }
  761.         catch (SQLException e) {
  762.             handleException (e);
  763.         }
  764.     }
  765.  
  766.     /**
  767.      * Gets the autoCommit property. If a database connection is established, the autoCommit
  768.      * property will be set to the autoCommit returned from java.sql.Connection.getAutoCommit().
  769.      *
  770.      * @see     java.sql.Connection#getAutoCommit
  771.      *
  772.      * @return      the value of the autocommit property.
  773.      * @exception   SQLException
  774.      */
  775.     public synchronized boolean isAutoCommit()
  776.     {
  777.         try {
  778.             if (getSQLConnection() != null) {
  779.                 autoCommit = getSQLConnection().getAutoCommit();
  780.             }
  781.         }
  782.         catch (SQLException e) {
  783.             System.out.println("Warning: isAutoCommit was returning a corrupted value: " + e.getMessage());
  784.         }
  785.         return autoCommit ;
  786.     }
  787.  
  788.     /**
  789.      * Sets the readOnly property. The property is bound, so a PropertyChangeEvent will
  790.      * be sent to registered ProperyChange listeners. This property will be sent to
  791.      * the database if a connection is established.
  792.      *
  793.      * @see         java.sql.Connection#setReadOnly
  794.      *
  795.      * @param       readOnly     The readOnly flag.
  796.      *
  797.      * @exception   SQLException
  798.      */
  799.     public synchronized void setReadOnly (boolean readOnly)
  800.         throws SQLException
  801.     {
  802.         if (java.beans.Beans.isDesignTime()) {
  803.             processPropertyChange(PROP_READONLY,
  804.                 new Boolean(this.readOnly),new Boolean(readOnly));
  805.         }
  806.         this.readOnly = readOnly ;
  807.         try {
  808.             if (lowLevelConnection != null) {
  809.                 lowLevelConnection.setReadOnly(readOnly) ;
  810.             }
  811.         }
  812.         catch (SQLException e) {
  813.             handleException (e);
  814.         }
  815.     }
  816.  
  817.     /**
  818.      * Gets the readOnly property. If a database connection is established, the readOnly
  819.      * property will be set to the readOnly returned from java.sql.Connection.isReadOnly().
  820.      *
  821.      * @see     java.sql.Connection#isReadOnly
  822.      *
  823.      * @return      the value of the readOnly property.
  824.      * @exception   SQLException
  825.      */
  826.     public synchronized boolean isReadOnly () throws SQLException
  827.     {
  828.         try {
  829.             if (getSQLConnection() != null)
  830.                 readOnly = getSQLConnection().isReadOnly();
  831.         }
  832.         catch (SQLException e) {
  833.             handleException (e);
  834.         }
  835.  
  836.         return readOnly ;
  837.     }
  838.  
  839.     /**
  840.      * Gets the readOnly property. If a database connection is established, the readOnly
  841.      * property will be set to the readOnly returned from java.sql.Connection.isReadOnly().
  842.      *
  843.      * @see     java.sql.Connection#isReadOnly
  844.      *
  845.      * @return      the value of the readOnly property.
  846.      * @exception   SQLException
  847.      */
  848.     public synchronized boolean getReadOnly () throws SQLException
  849.     {
  850.         return isReadOnly() ;
  851.     }
  852.  
  853.     /**
  854.      * Sets the transactionIsolation property. The property is bound, so a PropertyChangeEvent will
  855.      * be sent to registered ProperyChange listeners. This property will be sent to
  856.      * the database if a connection is established.
  857.      *
  858.      * @see         java.sql.Connection#setTransactionIsolation
  859.      *
  860.      * @param       TI     The transactionIsolationLevel.
  861.      *    TRANSACTION_NONE               = 0;
  862.      *    TRANSACTION_READ_UNCOMMITTED = 1;
  863.      *    TRANSACTION_READ_COMMITTED   = 2;
  864.      *    TRANSACTION_REPEATABLE_READ  = 4;
  865.      *    TRANSACTION_SERIALIZABLE     = 8;
  866.      *
  867.      * @exception   SQLException
  868.      */
  869.  
  870.     /**
  871.      * Transactions are not supported.
  872.      */
  873.     public final static int TRANSACTION_NONE         = 0;
  874.  
  875.     /**
  876.      * Dirty reads, non-repeatable reads and phantom reads can occur.
  877.      */
  878.     public final static int TRANSACTION_READ_UNCOMMITTED = 1;
  879.  
  880.     /**
  881.      * Dirty reads are prevented; non-repeatable reads and phantom
  882.      * reads can occur.
  883.      */
  884.     public final static int TRANSACTION_READ_COMMITTED   = 2;
  885.  
  886.     /**
  887.      * Dirty reads and non-repeatable reads are prevented; phantom
  888.      * reads can occur.
  889.      */
  890.     public final static int TRANSACTION_REPEATABLE_READ  = 4;
  891.  
  892.     /**
  893.      * Dirty reads, non-repeatable reads and phantom reads are prevented.
  894.      */
  895.     public final static int TRANSACTION_SERIALIZABLE     = 8;
  896.  
  897.     /**
  898.      * Use the drivers default level
  899.      */
  900.     public final static int TRANSACTION_DEFAULT         = 16;
  901.  
  902.  
  903.     public synchronized void setTransactionIsolation (int TI)
  904.         throws SQLException
  905.     {
  906.         if (java.beans.Beans.isDesignTime()) {
  907.             processPropertyChange(PROP_TRANS_ISOLATION,
  908.                 new Integer(transactionIsolation), new Integer(TI));
  909.         }
  910.         transactionIsolation = TI;
  911.         if (lowLevelConnection != null) {
  912.             try {
  913.                 int currentLevel = lowLevelConnection.getTransactionIsolation();
  914.                 if (transactionIsolation == TRANSACTION_DEFAULT) {
  915.                     transactionIsolation = defaultTransactionIsolation;
  916.                 }
  917.                 if (transactionIsolation != currentLevel) {
  918.                     lowLevelConnection.setTransactionIsolation(transactionIsolation) ;
  919.                 }
  920.             }
  921.             catch (SQLException e) {
  922.                 handleException (e);
  923.             }
  924.         }
  925.     }
  926.  
  927.     /**
  928.      * Gets the transactionIsolation property. If a database connection is established, the
  929.      * transactionIsolation property will be set to the value returned from
  930.      * java.sql.Connection.getTransactionIsolation().
  931.      *
  932.      * @see     java.sql.Connection#getTransactionIsolation
  933.      *
  934.      * @return      the value of the property.
  935.      * @exception   SQLException
  936.      */
  937.     public synchronized int getTransactionIsolation () throws SQLException
  938.     {
  939.         try {
  940.             if (getSQLConnection() != null)
  941.                 transactionIsolation = getSQLConnection().getTransactionIsolation();
  942.         }
  943.         catch (SQLException e) {
  944.             handleException (e);
  945.         }
  946.  
  947.         return transactionIsolation ;
  948.     }
  949.  
  950.     /**
  951.      * Sets the ConnectionClosed property. The property is constrained, so a VetoableChangeEvent will
  952.      * be sent to registered VetoableChange listeners. This property is a runtime only property.
  953.      * Setting this value to true will close the database connection. Registered listeners can
  954.      * veto the change if they are currently using the database connection.
  955.      *
  956.      * NOTE: Once BeanInfo object is created, this method should be renamed.!!!!
  957.      *
  958.      * @see         java.sql.Connection#close
  959.      *
  960.      * @param       close       The close flag. A true value will close the database connection.
  961.                                 A false value does nothing.
  962.      *
  963.      * @exception   SQLException
  964.      */
  965.     public synchronized void setConnectionClosed(boolean close)
  966.         throws PropertyVetoException, SQLException
  967.     {
  968.         //I don't like this code or method
  969.         //Revisit it.
  970.         try {
  971.             if(isConnectionClosed() == close || close == false)
  972.                 return ;
  973.  
  974.             if (getSQLConnection() != null) {
  975.                 processVetoableChange(JdbcConnection.PROP_CONNCLOSED,
  976.                                       new Boolean(false),new Boolean(true));
  977.                 getSQLConnection().close();
  978.                 lowLevelConnection = null ;
  979.                 processPropertyChange(JdbcConnection.PROP_CONNCLOSED,
  980.                                       new Boolean(false),new Boolean(true));
  981.             }
  982.         }
  983.         catch (SQLException e) {
  984.             handleException (e);
  985.         }
  986.     }
  987.  
  988.     /**
  989.      * Gets the connectionClosed property. If a database connection is established, the
  990.      * connectionClosed property will be set to the value returned from
  991.      * java.sql.Connection.isClosed() method. This property is a runtime property.
  992.      *
  993.      * @see     java.sql.Connection#isClosed()
  994.      *
  995.      * @return      the value of the connectionClosed property.
  996.      * @exception   SQLException
  997.      */
  998.     public synchronized boolean isConnectionClosed() throws SQLException
  999.     {
  1000.         boolean closed = true ;
  1001.         try {
  1002.             if (getSQLConnection() != null)
  1003.                 closed = getSQLConnection().isClosed();
  1004.                 if (closed)
  1005.                     lowLevelConnection = null;
  1006.  
  1007.         }
  1008.         catch (SQLException e) {
  1009.             handleException (e);
  1010.         }
  1011.  
  1012.         return closed ;
  1013.     }
  1014.  
  1015.  
  1016. //Event listeners
  1017.     /**
  1018.      * Implementation of the PropertyChangeListener interface. Connection objects that
  1019.      * have thier sharable property set to true, listen to other Connection objects in
  1020.      * order to be notified of property changes. Descendents of Connection who implement
  1021.      * the propertyChange method should call this method to maintain sharable logic.
  1022.      *
  1023.      * @see         Connection#setSharable
  1024.      *
  1025.      * @param       evt. The PropertyChangeEvent descibing the property that was changed.
  1026.      *
  1027.      */
  1028.     public void propertyChange (PropertyChangeEvent evt) {
  1029.         super.propertyChange(evt);
  1030.         if (evt.getSource() == this )
  1031.             return ;
  1032.     }
  1033.  
  1034.     //Event sources
  1035.     /**
  1036.      * Adds ConnectFailedListener to internal list so
  1037.      * that they could be sent ConnectFailedEvents.
  1038.      *
  1039.      * @param       listener. An object implementing the listener interface.
  1040.      *
  1041.      */
  1042.  
  1043.     public synchronized void addConnectFailedListener(ConnectFailedListener listener)
  1044.     {
  1045.         m_ConnectFailedListeners.addElement(listener);
  1046.     }
  1047.  
  1048.     /**
  1049.      * Removes ConnectFailedListeners from an internal list.
  1050.      * The object will no longer be sent ConnectFailedEvents.
  1051.      *
  1052.      * @param       listener. The ConnectFailedListener to remove.
  1053.      *
  1054.      */
  1055.  
  1056.     public synchronized void removeConnectFailedListener(ConnectFailedListener listener)
  1057.     {
  1058.         m_ConnectFailedListeners.removeElement(listener);
  1059.     }
  1060.  
  1061. //Serialization
  1062.     /**
  1063.      * Implementation of the ObjectInputValidation interface. During the de-serialization
  1064.      * process, registered ObjectInputValidation objects will have the validateObject method
  1065.      * called after the object and its graph are completely de-serialized. Connection
  1066.      * and their descendents should verify that the de-serialized object is of a version
  1067.      * that is compatible with the current implementation. Each level of the hierarchy should
  1068.      * provide their own versioning information. Descendents should check their version first
  1069.      * and then call their superclass's validateObject method. Connection will verify its version
  1070.      * and then check call JDBCBean.validateObject().
  1071.      *
  1072.      * If the autoStart property is true, doAutoStart() will be called by JDBCBean.
  1073.      *
  1074.      * @see JDBCBean#setAutoStart
  1075.      * @see JDBCBean#doAutoStart
  1076.      * @see Connection#connect
  1077.      *
  1078.      *
  1079.      * @exception   InvalidObjectException
  1080.      */
  1081.     public void validateObject () throws InvalidObjectException {
  1082.         //Check this object first, then call parent
  1083.         if (!version.equals(JdbcConnection.CURRENTVERSION))
  1084.             throw new InvalidObjectException ("Serialized version " +
  1085.                             version + "is not compatible with Connection version " +
  1086.                             JdbcConnection.CURRENTVERSION);
  1087.         //check superclass for valid object
  1088.         super.validateObject();
  1089.     }
  1090.  
  1091. //PROTECTED SECTION
  1092. //Static Declarations
  1093.     //Programatic property names -- Not localizable.
  1094.     protected static String PROP_DRIVERNAME     = "driverName" ;
  1095.     protected static String PROP_URL            = "url" ;
  1096.     protected static String PROP_USERNAME       = "userName" ;
  1097.     protected static String PROP_PASSWORD       = "password" ;
  1098.     protected static String PROP_CATALOG        = "catalog" ;
  1099.     protected static String PROP_AUTOCOMMIT     = "autoCommit" ;
  1100.     protected static String PROP_READONLY       = "readOnly" ;
  1101.     protected static String PROP_CONNCLOSED     = "connectionClosed" ;
  1102.     protected static String PROP_TRANS_ISOLATION = "transactionIsolationLevel";
  1103.  
  1104. //Constructors
  1105. //Methods
  1106.     /**
  1107.      * This method will be called by the connect() method to return a Properties object
  1108.      * containing java.sql.Driver specific connection properties. By default, this method
  1109.      * returns a Properties object with the userName and password properties. Descendents of
  1110.      * Connection can override this method to add additional Driver properties.
  1111.      *
  1112.      * @see     Connection#connect
  1113.      *
  1114.      * @return      A Properties object holding JDBC Connection properties.
  1115.      */
  1116.     protected java.util.Properties getConnectionProps () {
  1117.         java.util.Properties p = new Properties();
  1118.         if (getUserName() != null) {
  1119.             p.put("user", getUserName());
  1120.         }
  1121.         if (getPassword() != null) {
  1122.             p.put("password", getPassword());
  1123.         }
  1124.         return p ;
  1125.     }
  1126.     /**
  1127.      * This method will be called during the de-serialization process if the
  1128.      * autoStart property is true. Descendents can override this method if they want
  1129.      * to be notified when to go 'live'. Processing should be kept to a minimum or
  1130.      * done in a separate thread. Exceptions should be suppressed to allow
  1131.      * de-serialization to complete.
  1132.      *
  1133.      * Connection objects will attempt to establish a database connection by calling
  1134.      * the connect() method.  This is done in a separate thread to enable de-serialization
  1135.      * to continue.
  1136.      *
  1137.      */
  1138.     protected void doAutoStart() {
  1139.         //Connect to the database in another thread.
  1140.         //so that deserialization can continue.
  1141.         //Anonymous Inner class works well here. The class is defined
  1142.         // and started. The run method will simply callback to this
  1143.         //connection from its thread. The inner class has the connection
  1144.         // as its outer scope so it has complete access.
  1145.         new Thread() {
  1146.             public void run () {
  1147.                 try {
  1148.                     connect();
  1149.                 }
  1150.                 catch (SQLException e) {
  1151.                     //eat the exception so de-serialization will continue
  1152.                     printMessage ("Attempt to connect to database " + getURL() + " via Driver "
  1153.                                + getDriverName() + " failed: " + e.toString());
  1154.                     //insure handle is null
  1155.                     lowLevelConnection = null ;
  1156.                 }
  1157.             }  // end run()
  1158.         }.start();  // start() the thread running
  1159.     }
  1160.  
  1161. //Accessors/Modifiers
  1162.     /** This method returns the JDBC connection object controlling the database connection.
  1163.     */
  1164.     public synchronized java.sql.Connection getSQLConnection () {
  1165.         return lowLevelConnection ;
  1166.     }
  1167.  
  1168.  
  1169.  
  1170. //PRIVATE SECTION
  1171. //Static Declarations
  1172.     //Connection version string
  1173.     static private String CURRENTVERSION = "0.02" ;
  1174.  
  1175.  
  1176.  
  1177. //Serialization
  1178.     /**
  1179.      * During the serialization process, ObjectOutputStream will call this method.
  1180.      * By default, ObjectOutputStream.defaultWriteObject is called.
  1181.      *
  1182.      * @param       stream      The ObjectOutputStream controlling the serialization.
  1183.      */
  1184.  
  1185.     private void writeObject (ObjectOutputStream stream) throws IOException {
  1186.         stream.defaultWriteObject();
  1187.     }
  1188.  
  1189.     /**
  1190.      * During the de-serialization process, ObjectInputStream will call this method.
  1191.      * By default, Connection calls ObjectInputStream.defaultReadObject().
  1192.      *
  1193.      * @param       stream      The ObjectInputStream controlling the de-serialization.
  1194.      *
  1195.      * @exception   ClassNotFoundException, IOException
  1196.      */
  1197.     private void readObject (ObjectInputStream stream) throws ClassNotFoundException,
  1198.                                                               IOException {
  1199.         stream.defaultReadObject();
  1200.     }
  1201.  
  1202. //Members
  1203.     private String  version     = JdbcConnection.CURRENTVERSION ;
  1204.     private String  driverName  = "symantec.itools.db.jdbc.Driver";
  1205.     private String  url         = "";
  1206.     private String  userName    = "";
  1207.     private String  password    = "";
  1208.     private boolean autoCommit  = true;
  1209.     private int     transactionIsolation = TRANSACTION_DEFAULT;
  1210.     private transient int     defaultTransactionIsolation = TRANSACTION_DEFAULT;
  1211.     private boolean readOnly    = false;
  1212.     private String  catalog     = "";
  1213.     private String  identifier  = "jdbcConnection1";
  1214.     private transient Vector  m_ConnectFailedListeners = new Vector();
  1215.  
  1216.     private transient Stack m_AutoCommitMode = new Stack();
  1217.     private transient Stack m_TransactionState = new Stack();
  1218.     private transient Statement m_Statement = null;
  1219.  
  1220.     //Reference to JDBC Connection object.
  1221.     private transient java.sql.Connection lowLevelConnection = null;
  1222. }