package com.fuse.storage.sql;

/*
	FUSE Light Server - multiuser server application
	Copyright (C) 2000 Aapo Kyrola / Sulake Oy  Helsinki, Finland

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


import java.io.*;
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
import java.sql.*;

import com.fuse.*;
import com.fuse.storage.*;

/**
  * SQLDatabase implements a simple database which stores data into SQL database thru JDBC.
  * @see com.fuse.storage.FUSEDataObject
  * @author Jan-Mikael Lepisto / Aapo Kyrola
  */

public class SQLDatabaseImpl extends UnicastRemoteObject implements FUSEDatabase {
	
    // Connection has same role than File in filesystem package.
    // protected File rootDirectory;
    protected DatabaseConnectionManager connMgr;
    
    protected Hashtable classCache = new Hashtable(10);
	protected Vector objectPackages = new Vector(2);
	protected Properties properties = new Properties();
    protected static SQLDatabaseImpl localInstance;
    
    public SQLDatabaseImpl(String propFile) throws Exception {
	connMgr = DatabaseConnectionManager.getInstance();
	properties.load(new FileInputStream(propFile));
	if (connMgr == null) Log.error("Could not get a ConnectionManager");
	
	/* Dataobject packages */
	  String ipackage;
	  int i = 0;
		objectPackages.addElement("com.fuse.storage.sql.data_objects");
		while((ipackage = properties.getProperty("database.object_package." + (i++))) != null) {
			objectPackages.addElement(ipackage);
			Log.status("Added object package: " + ipackage);
		}
		
    }
    
    
    /**
     * Starts up the SQLDatabaseImpl
     * Usage: java .com.fuse.storage.SQL.SQLDatabaseImpl [rmiUrl]
     */
    public static void main(String[] args) {
	try {
	    SQLDatabaseImpl instance = new SQLDatabaseImpl(args[1]);
	    Naming.rebind(args[0], instance);
	    FUSEEnvironment.init(args[1]);
	  
			
	    Log.status("SQLDatabaseImpl up and running in address:" + args[0]);
	} catch (Exception e) {
	    Log.error(e);
	    Log.status("Usage: java .com.fuse.storage.sql.SQLDatabaseImpl [rmiUrl] [properties]");
	}
    }
    

	public static SQLDatabaseImpl getLocalInstance() {
		try {
			if (localInstance == null) localInstance = new SQLDatabaseImpl("./db.properties");
		} catch (Exception e) {;}
		return localInstance;
	}
	
    /**
	  * Constructs a FUSEDataObject of given type, which
	  * is compatible with the database implementation.
	  * The loaded object is typically named <impl package>.<type><impl type>:
	  * for example, if type is "FUSEUser" and we are using SQL implementation,
	  * the class is com.fuse.storage.sql.FUSEUserSQL
	  * @param type object type, such as "FUSEUser" or "logentry"
	  * @throws java.lang.ClassNotFoundException if object not found
	  */
	public FUSEDataObject createNew(String type) throws ClassNotFoundException, RemoteException {
		Class classC = (Class) classCache.get(type);
		/* Loads and instantiates the class if not found from instancecache */
		if (classC == null) {
			try {
				for(int i=0; i<objectPackages.size(); i++) {
					try {
						Log.status(objectPackages.elementAt(i));
						classC =  Class.forName((String) objectPackages.elementAt(i) + ".SQL" + type);
						break;					} catch(ClassNotFoundException ce) {}
				}
			} catch (Exception e) {
				Log.error(e);
				throw new ClassNotFoundException();
			}
			if (classC== null) throw new ClassNotFoundException();
			classCache.put(type, classC);
		}
		try {
			return (FUSEDataObject) classC.newInstance();
		} catch (Exception e) {
			Log.error(e);
		}
		throw new ClassNotFoundException();
	}
	
	/** 
	  * Stores an object
	  * @param dbObject the object to store
	  * @returns the stored object  (use it after this method)
	  * @throws com.fuse.storage.DatabaseException if insertion failed
	  */
	public FUSEDataObject insert(FUSEDataObject dbObject) throws DatabaseException, RemoteException {
	    Connection con = null;
	    try {
	    	con = connMgr.getConnection();
		    if (con == null) {
			Log.error("Can't get connection");
			throw new DatabaseException("SQLDatabaseImpl:insert()");
		    }
		    
		    ((SQLDataObject) dbObject).insert(con);
		    return dbObject;
		} finally {
	    	connMgr.freeConnection(con);
	    }
	    
	}
	
	/**
	  * Deletes an object from the database
	  * @param dbObject the data object to delete
	  * @throws com.fuse.storage.DatabaseException if deleting failed
	  */
	public void delete(FUSEDataObject dbObject) throws DatabaseException, RemoteException {
	    Connection con = null;
		try {
	    	con = connMgr.getConnection();
	    	if (con == null) {
				Log.error("Can't get connection");
				throw new DatabaseException("");
	    	}
	    	((SQLDataObject) dbObject).delete(con);
	    	
	    } finally {
	    	connMgr.freeConnection(con);
	    }
	}
	
	/**
	  * Updates an object in the database
	  * @param dbObject the data object to update
	  * @returns the updated object (use it after this method)
	  */
	public FUSEDataObject update(FUSEDataObject dbObject) throws DatabaseException, RemoteException {
	    Connection con = null;
		try {
	    	con = connMgr.getConnection();
	    	if (con == null) {
			Log.error("Can't get connection");
			throw new DatabaseException("");
	    	}
	    	((SQLDataObject) dbObject).update(con);
	    	return dbObject;
	    } finally {
	    	connMgr.freeConnection(con);
	    }
	}
	
	/**
	  * Makes a query of data object from the database. The actual query is 
	  * done by the ObjectQueryBean
	  * @param bean the objectQueryBean which implements the query
	  * @returns Vector the found objects
	  */
	public Vector query(ObjectQueryBean bean) throws DatabaseException, RemoteException {
	     Connection con = null;
	     try {
	     	con = connMgr.getConnection();
	    	Vector retval = null;
	   	 	if (con == null) {
				Log.error("Can't get connection");
				throw new DatabaseException("");
	    	}
	    	retval = ((SQLObjectQueryBean) bean).execute(con);
	    	
	   		return retval;
	   	} finally {
	   		connMgr.freeConnection(con);	   
	   	}
	}
    
	/**
	  * Makes a query which returns some numeric information.
	  * @param bean the bean which implements the query
	  * @returns an object which knows the resuls
	  */
	public Object querySummary(SummaryQueryBean bean) throws DatabaseException, RemoteException {
		Connection con = null;
		try {   
	   		con = connMgr.getConnection();
	    	Object retval = null;
	    	if (con == null) {
				Log.error("Can't get connection");
				throw new DatabaseException("");
	    	}
	    	retval = ((SQLSummaryQueryBean) bean).execute(con);
	   
	    	return retval;
	    } finally {
	    	 connMgr.freeConnection(con);	   
	    }
	}


}




