package org.jcon.df;

import org.jcon.ba.system.BeanActionClose;
import org.jcon.ba.system.BeanActionStart;
import org.jcon.df.work.DatabaseUtil;
import org.jcon.util.GenLib;
import org.jcon.util.msg.Message;
import org.jcon.util.msg.MessageListener;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.util.Enumeration;
import java.util.Hashtable;

/**
 * This class exposes a subset of a remote Datastore to a
 * client. Thus it is the client Datastore entry point.
 * <p>
 * There is no initialization or start() needed. The remote
 * server is not contacted until needed, such as when a
 * database is requested. This allows fast system startup.
 *
 * @author Jack Harich 
 */
public class DatastoreServices implements
    BeanActionStart, BeanActionClose, MessageListener {

//---------- Private Fields ------------------------------
private DatastoreRemote datastore;
private String          remoteName = "DatastoreRemote";
private String          hostName = ""; // Nothing for localhost
// ** SystemUser mod in progress
//private ***SystemUser      systemUser;

// These are prototypes and are cloned for createDatabase()
// Key = dbName, Object = Database
private Hashtable databases = new Hashtable();

//---------- Initialization ------------------------------
/**
 * Unit test. Currently args are ignored.
 */
public static void main(String args[]) {
    DatastoreServices services = new DatastoreServices();
    services.start();
    //services.runUnitTest();
}
//---------- BeanActionStart Implementation --------------
/**
 * Starts the steps to make services available. This is
 * mainly getting the remote Datastore.
 */
public void start() {
    // NO LONGER NEEDED, which is good ***
    //boolean successful = lookupRemoteDatastore();
    //if (successful) runUnitTest(); // ***test***
}
//---------- BeanActionClose Implementation --------------
public String canClose() {
    return null; // Yes
}
// Release all databases to hasten server resources reclamation
public void close() {
    Enumeration enum = databases.elements();
    while (enum.hasMoreElements())  {
        Database database = (Database)enum.nextElement();
        try {
            database.getPerformer().release();
        } catch(RemoteException ex) {
            String dbName = database.getName();
            GenLib.exception("DatastoreServices.close()",
                "Cannot release database named '" + dbName + "'.", ex);
        }
    }
}
//---------- MessageListener Implementation --------------
public void processMessage(Message message) {
    String name = message.getName();
    //print(".processMessage() - name " + name);

    // *** Soon to be obsolete due to ContainerServices
    if (name == "AcquireDatastoreServices") {
        print(".processMessage() - setting Services");
        message.set("Services", this);

    } else if (name == "SystemUserChanged") {
        // ***systemUser = (SystemUser)message.get("SystemUser");
        //print(" - set systemUser to UserID " + systemUser.getUserID());
    }
}
public String[] loadMessageInterests() {
    return new String[] {"AcquireDatastoreServices",
        "SystemUserChanged"};
}
//---------- Properties ----------------------------------
//----- remoteName
/**
* Sets the DatastoreRemote RMI remote name. The default
* is "DatastoreRemote".
*/
public void setRemoteName(String remoteName) {
    this.remoteName = remoteName;
}
public String getRemoteName() {
    return remoteName;
}
//----- hostName
/**
 * Sets the host name for remote use. The default is ""
 * which uses "localhost". Otherwise supply a host name
 * such as "rmi://infinity/".
 */
public void setHostName(String hostName) {
    this.hostName = hostName;
}
public String getHostName() {
    return hostName;
}
//---------- Public Methods ------------------------------
//----- Exposed Datastore methods, remote

// Returns null if failure ReadWrite.
public Database createDatabase(String dbName) {

checkDatastore();
Database database = (Database)databases.get(dbName);
if (database == null) {
    try {
        //String userID = null;
        //if (systemUser != null) userID = systemUser.getUserID();
        database = datastore.createDatabase(dbName);
        database.setDatastoreServices(this);
        databases.put(dbName, database);
        print(".createDatabase() - created '" + dbName + "'");
    } catch(Exception ex) {
        GenLib.exception("DatastoreServices.createDatabase()",
            "Cannot create database named '" + dbName + "'.", ex);
        return null;
    }
}
return (Database)database.clone();
} // End method

// Returns null if failure
public String[] loadDatabaseNames() {
    checkDatastore();
    try {
        return datastore.loadDatabaseNames();
    } catch(RemoteException ex) {
        GenLib.exception("DatastoreServices.loadDatabaseNames()",
            "Cannot load database names.", ex);
        return null;
    }
}
public Entity getEntity(String schemaName, String entityName) {
    // *** REMOVE AFTER TESTING Now done by database
    //print(".getEntity() - no longer used, do not call");
    //return null;
    try {
        return datastore.getEntity(schemaName, entityName);
    } catch(RemoteException ex) {
        GenLib.exception("DatastoreServices.getEntity()",
            "Cannot get entity '" + entityName + "' in schema '" + schemaName + "'.", ex);
        return null;
    }
}
//----- Other
public Schema getSchema(String dbName) {
    Database database = (Database)databases.get(dbName);
    if (database == null) database = createDatabase(dbName);
    return database.getSchema();
}
// *** May be obsolete
public String runUnitTest() {
    checkDatastore();
    String testName = null;

        testName = "createDatabase";
        Database database = this.createDatabase("atsdr");
            //"ATSDR_LocalAccessProduction");
        if (database == null) return "Database create failure";;

        testName = "testConnection";
        String testConnResult = database.testConnection();
        print(" - testConnection result = \n" + testConnResult);

        testName = "countRows";
        DatabaseUtil util = new DatabaseUtil(database);
        int count = util.countRows("SysUser", null);
        print(" - SysUser count = " + count);

        return "---> Test completed successfully";
}
//---------- Private Methods -----------------------------
// Set datastore. Could be on demand.
private boolean lookupRemoteDatastore() {
    String name = hostName + remoteName;
    Object remoteObject = null;
    try {
        // All RMI clients should always install a SecurityManager
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }
        // Here we use RMI to get a remote object
        // Note we must use the interface, not the class
        print(" - Looking up '" + name + "', please wait...");
        remoteObject = Naming.lookup(name);
        datastore = (DatastoreRemote)remoteObject;
        print("   RMI Naming lookup complete");
        return true;

    } catch(Exception ex) {
        GenLib.exception("DatastoreServices.lookupRemoteDatastore()",
            "Lookup failure for '" + name + "', remoteObject = " + remoteObject, ex);

        return false;
    }
}
private void checkDatastore() {
    if (datastore == null) lookupRemoteDatastore();
}
//--- Std
private static void print(String text) {
    System.out.println("DatastoreServices" + text);
}


} // End class
