package javacli;
import java.util.Hashtable;
import java.io.*;
import java.util.Date;
import java.util.HashMap;
import java.lang.reflect.*;
import java.net.*;
/**
* Gigabase interface responsible for managing connection with GigaBASE server.
* Also it implements insert commit, rollback operations
*/
public class Connection {
/**
* Open onnection with server
* @param hostAddress string with server host name
* @param hostPort integer number with server port
*/
public void open(String hostAddress, int hostPort) {
open(hostAddress, hostPort, "guest", "");
}
/**
* Open connection with server
* @param hostAddress string with server host name
* @param hostPort integer number with server port
* @param userName user name
* @param userPassword password
*/
public void open(String hostAddress, int hostPort, String userName, String userPassword) {
address = hostAddress;
port = hostPort;
user = userName;
password = userPassword;
try {
socket = null;
if (hostAddress.equals("localhost")) {
try {
socket = new LocalSocket(hostAddress, hostPort);
} catch (Throwable x) {}
}
if (socket == null) {
socket = new Socket(hostAddress, hostPort);
}
try {
socket.setTcpNoDelay(true);
} catch (NoSuchMethodError er) {}
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException x) {
throw new CliError("Faield to open database: " + x);
}
ComBuffer buf = new ComBuffer(cli_cmd_login);
buf.putAsciiz(userName);
buf.putAsciiz(password);
sendReceive(buf);
}
/**
* Add package name to the list of registered package. The name of the each registered
* package will be appended to the table name when Java class for the database table is
located.
* @param pkName - name of the package
*/
public void addPackage(String pkName) {
if (pkgs == null) {
pkgs = new String[1];
} else {
String np[] = new String[pkgs.length+1];
System.arraycopy(pkgs, 0, np, 0, pkgs.length);
pkgs = np;
}
pkgs[pkgs.length-1] = pkName;
}
/**
* Close connection with server
*/
public void close() {
if (socket == null) {
throw new CliError("Session is not opened");
}
if (pool != null) {
pool.releaseConnection(this);
return;
}
ComBuffer buf = new ComBuffer(cli_cmd_close_session);
send(buf);
try {
socket.close();
} catch (IOException x) {
socket = null;
throw new CliError("Close failed: " + x);
}
socket = null;
}
/**
* Create select statement.
* @param sql - SubSQL select statement with parameters. Paameters should be
* started with <code>%</code> character. Each used paramter should be set
* before execution of the statement.
*/
public Statement createStatement(String sql) {
if (socket == null) {
throw new CliError("Session is not opened");
}
return new Statement(this, sql, ++nStatements);
}
/**
* Commit current transaction
*/
public void commit() {
if (socket == null) {
throw new CliError("Session is not opened");
}
sendReceive(cli_cmd_commit, 0);
}
/**
* Release all locks set by the current transaction
*/
public void precommit() {
if (socket == null) {
throw new CliError("Session is not opened");
}
sendReceive(cli_cmd_precommit, 0);
}
/**
* Rollback curent transaction.Al changes made by current transaction are lost.
*/
public void rollback() {
if (socket == null) {
throw new CliError("Session is not opened");
}
sendReceive(cli_cmd_abort, 0);
}
/**
* Insert object in the database. There is should be table in the database with
* name equal to the full class name of the inserted object (comparison is
* case sensitive). GigaBASE will store to the database all non-static and
* non-transient fields from the class. All Java primitive types,
* <code>java.lang.String</code>, arrays of primitive types or strings, <code>java.util.Date</code>
* are supported by GigaBASE. If <code>int</code> field is marked as <code>volatile</code>, it
* is assumed to be autoincremented field - unique value to this field is assigned automatically
* by GigaBASE.
*
* @param obj - object to be inserted inthe database
* @return reference to th inserted object
*/
public Reference insert(Object obj) {
if (socket == null) {
throw new CliError("Session is not opened");
}
Class clazz = obj.getClass();
String className = clazz.getName();
className = className.substring(className.lastIndexOf('.')+1);
TableDescriptor tableDesc;
synchronized (tableHash) {
tableDesc = (TableDescriptor)tableHash.get(className);
if (tableDesc == null) {
tableDesc = new TableDescriptor(clazz);
tableHash.put(className, tableDesc);
}
}
ComBuffer buf = new ComBuffer(cli_cmd_prepare_and_insert);
buf.putAsciiz("insert into " + className);
buf.putByte(tableDesc.nColumns);
tableDesc.writeColumnDefs(buf);
tableDesc.writeColumnValues(buf, obj);
send(buf);
receive(buf, 12);
int rc = buf.getInt();
if (rc == cli_ok) {
int rowid = buf.getInt();
int oid = buf.getInt();
if (tableDesc.autoincrement) {
try {
for (int i = tableDesc.nColumns; --i >= 0;) {
if (tableDesc.types[i] == Connection.cli_autoincrement) {
tableDesc.columns[i].setInt(obj, rowid);
}
}
} catch (IllegalAccessException x) {
throw new CliError("Failed to assigned value to autoincremented field: " + x);
}
}
return (oid != 0) ? new Reference(oid) : null;
} else {
throw new CliError("Insert object operation failed with status " + rc);
}
}
/**
* Create table matching specified class. Name of the table is equal
* to class name without any package prefixes. This table will include columns
* corresponsinf to all non-static and non-transient fields of specified class and base classes
* of this class.
* @param cls Java class for which table should be created.
* @return <code>true</code> if table sucessfully created, <code>false</code> if table already exists
* @exception throws CliError exception in case of all other errors (except table already exists)
*/
public boolean createTable(Class cls)
{
return createTable(cls, null);
}
/**
* Create table matching specified class. Name of the table is equal
* to class name without any package prefixes. This table will include columns
* corresponsinf to all non-static and non-transient fields of specified class and base classes
* of this class.
* @param cls Java class for which table should be created.
* @param referenceMap map to provide names of referenced tables for reference fields.
* Key of this map is field name.
* @return <code>true</code> if table sucessfully created, <code>false</code> if table already exists
* @exception throws CliError exception in case of all other errors (except table already exists)
*/
public boolean createTable(Class cls, HashMap referenceMap)
{
int i;
if (socket == null) {
throw new CliError("Session is not opened");
}
Class c;
int nColumns = 0;
for (c = cls; c != null; c = c.getSuperclass()) {
Field[] classFields = c.getDeclaredFields();
for (i = 0; i < classFields.length; i++) {
if ((classFields[i].getModifiers() & (Modifier.TRANSIENT|Modifier.STATIC)) == 0) {
nColumns += 1;
}
}