FileDocCategorySizeDatePackage
MsqlConnection.javaAPI DocExample10297Tue Jan 01 00:00:00 GMT 1980COM.imaginary.sql.msql

MsqlConnection.java

/* Copyright (c) 1997 George Reese */
package COM.imaginary.sql.msql;

import java.io.IOException;
import java.net.Socket;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Properties;

/**
 * The MsqlConnection class is an implementation of the JDBC Connection
 * interface that represents a database transaction.  This class should
 * never be directly instantiated by an application, but instead should
 * be gotten by calling DriverManager.getConnection().<BR>
 * Last modified 97/04/19
 * @version @(#) MsqlConnection.java 1.3@(#)
 * @author George Reese (borg@imaginary.com)
 * @see java.sql.DriverManager#getConnection
 */
public class MsqlConnection implements Connection {
    private String           catalog    = null;
    private boolean          connecting = true;
    private Socket           connection = null;
    private Statement        current    = null;
    private MsqlInputStream  input      = null;
    private int              isolation  = 0;
    private MsqlOutputStream output     = null;
    private String           user       = null;
    private int              version    = 0;
    private SQLWarning       warnings   = null;

    // Since this should only be instantiated by MsqlDriver, this is
    // package visibility
    MsqlConnection(String host, int port, String db, Properties p)
    throws SQLException {
	super();
	user = (String)p.get("user");
	if( user == null || user.equals("") ) {
	    user = "nobody";
	}
	connect(host, port, user);
	selectDatabase(db);
	synchronized( this ) {
	    connecting = false;
	    notify();
	}
    }

    /**
     * @return true if this Connection has been closed
     * @exception java.sql.SQLException never thrown
     */
    public synchronized boolean isClosed() throws SQLException {
	awaitConnection();
	return (connection == null);
    }

    /**
     * @return false--always
     */
    public boolean isReadOnly() throws SQLException {
	return false;
    }

    /**
     * This method always returns true since mSQL is always in auto-commit
     * mode.
     * @return true--always
     * @exception java.sql.SQLException never thrown
     */
    public boolean getAutoCommit() throws SQLException {
	return true;
    }

    /**
     * This method will thrown an exception if you try to turn auto-commit
     * off since JDBC does not support transactional logic.
     * @param b should always be true
     * @exception java.sql.SQLException thrown if the param is false
     */
    public void setAutoCommit(boolean b) throws SQLException {
	if( b ) {
	    return;
	}
	throw new SQLException("mSQL must always be auto-commit = true.");
    }

    /**
     * Provides the catalog name.  mSQL does not support catalogs,
     * however.
     * @exception java.sql.SQLException never thrown
     */
    public synchronized String getCatalog() throws SQLException {
	return catalog;
    }

    /**
     * Sets the catalog name for this connection.  mSQL does not support
     * catalogs, so this method is a NO-OP.
     * @exception java.sql.SQLException never thrown
     */
    public synchronized void setCatalog(String str) throws SQLException {
	catalog = str;
    }

    /**
     * This method is not yet implemented.
     */
    public DatabaseMetaData getMetaData() throws SQLException {
	return null;
    }

    /**
     * mSQL does not support read-only mode.
     * @exception java.sql.SQLException alway thrown
     */
    public void setReadOnly(boolean b) throws SQLException {
	throw new SQLException("mSQL does not support read-only mode.");
    }

    /**
     * @return the transaction isolation, not supported by mSQL
     * @exception java.sql.SQLException never thrown
     */
    public synchronized int getTransactionIsolation() throws SQLException {
	return isolation;
    }

    /**
     * This is not supported by mSQL, thus this is a NO-OP.
     * @exception java.sql.SQLException never thrown
     */
    public synchronized void setTransactionIsolation(int x)
    throws SQLException {
	isolation = x;
    }

    /**
     * @return the user name used for this Connection
     * @exception java.sql.SQLException thrown if the connection is never made
     */
    public String getUser() throws SQLException {
	awaitConnection();
	return user;
    }

    /**
     * @return the SQLWarning chain for this Connection
     * @exception java.sql.SQLException never thrown
     */
    public synchronized SQLWarning getWarnings() throws SQLException {
	return warnings;
    }

    // Tests if the statement is in the middle of a connect.  If so, it
    // will await connection.
    private void awaitConnection() throws MsqlException {
	synchronized( this ) {
	    while( connecting ) {
		try {
		    wait();
		}
		catch( InterruptedException e ) {
		    if( connecting ) {
			throw new MsqlException(e);
		    }
		}
	    }
	}
    }
    
    // Cleans up when the connection is lost
    private void cleanConnection() {
	synchronized( this ) {
	    if( current != null ) {
		try {
		    current.close();
		}
		catch( SQLException e ) {
		    e.printStackTrace();
		}
		current = null;
	    }
	    connecting = false;
	    connection = null;
	}
	input = null;
	output = null;
	user = null;
    }

    /**
     * Sets the SQLWarning chain to null.
     * @exception java.sql.SQLException never thrown
     */
    public synchronized void clearWarnings() throws SQLException {
	warnings = null;
    }

    /**
     * Closes this database connection.  It will also close any associated
     * Statement instance.
     * @exception java.sql.SQLException thrown if problems occur
     */
    public synchronized void close() throws SQLException {
	Exception except = null;

	awaitConnection();
	if( isClosed() ) {
	    throw new SQLException("This Connection is already closed.");
	}
	// Any current statement has ownership of the streams
	// We therefore have to close *before* sending data
	if( current != null ) {
	    current.close();
	    current = null;
	}
	// Let mSQL know we are closing down and close the output stream
	try {
	    output.writeAsciiString("1");
	    output.flush();
	    output.close();
	    output = null;
	}
	catch( IOException e ) {
	    output = null;
	    except = e;
	}
	// close the input stream
	try {
	    input.close();
	    input = null;
	}
	catch( IOException e ) {
	    input = null;
	    except = e;
	}
	// close the connection
	try {
	    connection.close();
	}
	catch( IOException e ) {
	    connection = null;
	    throw new MsqlException(e);
	}
	if( except != null ) {
	    throw new MsqlException(except);
	}
    }

    /**
     * This is a NO-OP for mSQL.  All statements are always auto-committed.
     * @exception java.sql.SQLException never thrown
     */
    public void commit() throws SQLException {
    }

    // Performs the actual database connection and creates the
    // MsqlOutputStream and MsqlInputStream used to talk to mSQL
    private void connect(String host, int port, String user)
    throws SQLException {
	try {
	    String tmp;
	    
	    connection = new Socket(host, port);
	    input = new MsqlInputStream(connection.getInputStream());
	    output = new MsqlOutputStream(connection.getOutputStream());
	    tmp = input.readAsciiString();
	    // Check for mSQL version
	    if( tmp.startsWith("0:22:") ) {
		version = 2; // version 2.0
	    }
	    else if( tmp.startsWith("0:6:") ) {
		version = 1; // version 1.0.x
	    }
	    else {
		throw new SQLException("Unsupported mSQL version.");
	    }
	    // Check if user was validated
	    output.writeAsciiString(user);
	    tmp = input.readAsciiString();
	    if( !tmp.startsWith("-100:") ) {
		throw new SQLException("Access to server denied.");
	    }
	}
	catch( IOException e ) {
	    cleanConnection();
	    throw new SQLException("Connection failed.");
	}
    }

    /**
     * This JDBC method creates an instance of MsqlStatement and
     * returns it.  If there are currently any open MsqlStatement's, it
     * will close them.  Not that mSQL does not provide a way to
     * interrupt executing statements, so it has to wait for any
     * pending statements to finish before closing them.
     * @return a new MsqlStatement instance
     * @exception java.sql.SQLException an error occurred in creating the
     * Statement instance, likely raised by the constructor
     */
    public Statement createStatement() throws SQLException {
	awaitConnection();
	synchronized( this ) {
	    if( current != null ) {
		try {
		    current.close();
		}
		catch( SQLException e ) {
		    e.printStackTrace();
		}
	    }
	    current = new MsqlStatement(this, input, output);
	    return current;
	}
    }

    /**
     * This gives the driver an opportunity to turn JDBC compliant SQL
     * into mSQL specific SQL.  My feeling is why bother.
     * @exception java.sql.SQLException never thrown
     */
    public String nativeSQL(String sql) throws SQLException {
	return sql;
    }

    /**
     * Callable statements are not supported by mSQL.  This will therefore
     * always throw an exception.
     */
    public CallableStatement prepareCall(String sql) throws SQLException {
	throw new SQLException("mSQL does not support stored procedures.");
    }

    /**
     * Prepared statements are not supported by mSQL.  This will
     * therefore always throw an exception.
     */
    public PreparedStatement prepareStatement(String sql) throws SQLException {
	throw new SQLException("mSQL does not support prepared statements.");
    }

    /**
     * This method always errors since you cannot rollback an mSQL
     * transaction.
     */
    public void rollback() throws SQLException {
	throw new SQLException("mSQL exception: mSQL does not support " +
			       "rollbacks.");
    }

    // Tells mSQL which database we want to use
    private void selectDatabase(String database) throws MsqlException {
	String tmp;

	try {
	    output.writeAsciiString("2 " + database);
	    tmp = input.readAsciiString();
	    if( tmp.startsWith("-1:") ) {
		throw new MsqlException(tmp);
	    }
	}
	catch( IOException e ) {
	    cleanConnection();
	    throw new MsqlException(e);
	}
    }
}