package edu.vt.marian.server; import java.io.*; import java.net.*; import java.lang.*; import java.util.*; import edu.vt.marian.common.*; import edu.vt.marian.uip.*; /** class name: session_table class description: this class is used by session manager object to do the mapping between client id, user id and session id, it can be considered as a table with four columns, the first is client id, the second is user id, the third is session id and the last is session object. uses the services of class(es): designer(s): Jianxin Zhao (jxzhao@csgrad.cs.vt.edu) implementator(s): Ning Chai (nchai@csgrad.cs.vt.edu) finished time: Nov 26th, 1998 known bugs: JDK version: 1.1.5 side effects: */ public class session_table { public final static int OK = 0; public final static int INVALID_TIME = -1; public final static int INVALID_NUMBER = 1; public final static int NULL_RPC_FUNCTION = 2; public final static int NULL_PARAMETER = 3; public final static int INVALID_PARAMETER = 4; public final static int PROCESS_CALL_FROM_CLIENT_FAILED = 5; public final static int PROCESS_CALL_FROM_C_FAILED = 6; public final static int SESSION_TABLE_OVERFLOWED = 7; public final static int NO_UNUSED_SESSION_ID = 8; public final static int CANNOT_FIND_SESSION = 9; public final static int PROCESS_CALL_FROM_SESSION_TABLE_TO_CLIENT_FAILED = 10; public final static int PROCESS_CALL_FROM_SESSION_TABLE_TO_C_FAILED = 11; private Vector entries; private int max_number_sessions; private session_manager sm; private resource_manager rm; private int max_session_id; private session_manage sm_thread; private boolean id_wrap_flag; private ReaderWriterMutex reader_writer; // used to sychronize the read and write operation on session_table /** this is just used for debugging */ Debug debug; /** method description: this constructor will create a session table object, this object is created by the specified session manager object and the specified directory contains all the configuration information about this object uses the services of class(es): input parameter(s): sm -- this is the session manager object which created this object file_name -- this is the name of the directory which contains all the configuration information of this object debug -- used for debugging output parameter(s): none return value: none synchronization: none */ public session_table(session_manager sm, String dir_name, resource_manager rm, Debug debug) { BufferedReader in_file = null; String line = null; StringTokenizer token_line = null; String s = null; String s1 = null; init(); this.debug = debug; this.sm = sm; if (sm == null) { debug.dumpTrace("Class:session_table Method:constructor Session_manager is null."); } this.rm = rm; if (rm == null) { debug.dumpTrace("Class:session_table Method:constructor Resource_manager is null."); } if ((dir_name == null) || dir_name.equals("")) { debug.dumpTrace("Class:session_table Method:constructor Dir_name is null or empty."); } else { try { in_file = new BufferedReader(new FileReader(dir_name.concat("config"))); line = in_file.readLine(); while (line != null) { token_line = new StringTokenizer(line," ",false); if (token_line.countTokens() > 1) { // empty lines and lines with only one token will be ignore s = token_line.nextToken(); if (!s.startsWith("#")) //Ignore comments(begin with "#") { if (s.equals("max_number_sessions")) { s1 = token_line.nextToken(); try { max_number_sessions = Integer.parseInt(s1); if (max_number_sessions < 0) { max_number_sessions = 100; debug.dumpTrace("Class:session_table Method:constructor max_number_sessions must be a positive integer."); } } catch (Exception e) { max_number_sessions = 100; debug.dumpTrace("Class:session_table Method:constructor max_number_sessions must be an integer."); } } } // end if (!s.startsWith("#")) } // end if (token_line.countTokens() > 1) line = in_file.readLine(); } // end while in_file.close(); } catch (IOException e) { debug.dumpTrace("Class:session_table Method:constructor File open error."); } } reader_writer = new ReaderWriterMutex(debug); sm_thread = new session_manage(dir_name.concat("session_manage"), this, debug); sm_thread.start(); return; } /** method description: this method will tell the current number of sessions in this table uses the services of class(es): input parameter(s): none output parameter(s): none return value: the current number of sessions in this table as an integer synchronization: none */ public int get_number_sessions() { return entries.size(); } /** method description: this method will tell at most how many sessions can be stored in this object. uses the services of class(es): input parameter(s): none output parameter(s): none return value: the maximum possible number of sessions allowed in this table as an integer synchronization: none */ public int get_max_number_sessions() { return max_number_sessions; } /** method description: this method will set the maximum possible number of sessions can be stored in this table uses the services of class(es): input parameter(s): number -- this will become the new upper limit output parameter(s): none return value: OK -- the number has been set correctly INVALID_NUMBER -- the number is negative and doesn't make sense other -- synchronization: none */ public int set_max_number_sessions(int number) { if (number <= 0) { debug.dumpTrace("Class:session_table Method:set_max_number_sessions Invalid number."); return INVALID_NUMBER; } max_number_sessions = number; return OK; } /** method description: this method will delete all the session entries which has no activity longer than the specified time uses the services of class(es): input parameter(s): time -- the threhold value used to delete sessions, the unit is ms output parameter(s): none return value: non negative number -- number of sessions deleted INVALID_TIME -- the time is negative and doesn't make sense other -- synchronization: writer method */ public int delete_old_sessions(long time) { int i; int number_deleted = 0; session_table_entry ste = null; if (time < 0) { debug.dumpTrace("Class:session_table Method:delete_old_sessions Invalid time."); return INVALID_TIME; } reader_writer.writer_enter(); for (i = entries.size() - 1; i >= 0; i--) { ste = (session_table_entry)(entries.elementAt(i)); if (ste.get_session().get_inactive_time() >= time) { ste.get_session().exit("time out"); entries.removeElementAt(i); number_deleted++; } } reader_writer.writer_exit(); return number_deleted; } /** method description: this method will process the function sent by the client (currently webgate). uses the services of class(es): input parameter(s): client_id -- identifies which client (webgate) send this function rf -- the rpc function need to be processed output parameter(s): none return value: OK -- the function has been processed correctly NULL_RPC_FUNCTION -- parameter rf is null NULL_PARAMETER -- can't find parameter in rf INVALID_PARAMETER -- parameter's type of rf is invalid SESSION_TABLE_OVERFLOWED -- session table overflowed and can not create a new user PROCESS_CALL_FROM_CLIENT_FAILED -- error happens when session process the function NO_UNUSED_SESSION_ID -- not enough id available to use synchronization: writer method */ public int process_call_from_client(int client_id, rpc_function rf) { int i; parameter p = null; query query_obj = null; session_table_entry ste = null; session s = null; int user_id; if (rf == null) { debug.dumpTrace("Class:session_table Method:process_call_from_client rpc_function is null."); return NULL_RPC_FUNCTION; } p = rf.get_parameter(0); rf.delete_parameter(0); if (p == null) { debug.dumpTrace("Class:session_table Method:process_call_from_client Parameter is null."); return NULL_PARAMETER; } if (!p.get_type().equals("INTEGER")) { debug.dumpTrace("Class:session_table Method:process_call_from_client Invalid parameter type."); return INVALID_PARAMETER; } user_id = ((Integer)p.get_value()).intValue(); reader_writer.writer_enter(); for ( i = 0; i < entries.size(); i++) { ste = (session_table_entry)entries.elementAt(i); if ((ste.get_client_id() == client_id) && (ste.get_user_id() == user_id)) { if (ste.get_session().process_call_from_client(rf) == ste.get_session().OK) { reader_writer.writer_exit(); return OK; } else { reader_writer.writer_exit(); return PROCESS_CALL_FROM_CLIENT_FAILED; } } } // can't find corresponding session in the session table, so a new session must be created if (entries.size() >= max_number_sessions) { debug.dumpTrace("Class:session_table Method:process_call_from_client Session_table overflow."); reader_writer.writer_exit(); return SESSION_TABLE_OVERFLOWED; } if (id_wrap_flag == true) { for (i = 0; i < entries.size(); i++) { ste = (session_table_entry)entries.elementAt(i); if (ste.get_session().get_id() > i) { s = new session(this, i, rm, debug); sm.log_data_from_session_table(1,"session with id "+Integer.toString(i)+" created corresponds to client "+Integer.toString(client_id)+" user "+Integer.toString(user_id)); ste = new session_table_entry(client_id, user_id, s, debug); entries.insertElementAt(ste, i); if (s.process_call_from_client(rf) == s.OK) { reader_writer.writer_exit(); return OK; } else { reader_writer.writer_exit(); return PROCESS_CALL_FROM_CLIENT_FAILED; } } } // create a session using i as its id s = new session(this, i, rm, debug); sm.log_data_from_session_table(1,"session with id "+Integer.toString(i)+" created corresponds to client "+Integer.toString(client_id)+" user "+Integer.toString(user_id)); ste = new session_table_entry(client_id, user_id, s, debug); entries.addElement(ste); if (s.process_call_from_client(rf) == s.OK) { reader_writer.writer_exit(); return OK; } else { reader_writer.writer_exit(); return PROCESS_CALL_FROM_CLIENT_FAILED; } } else // id_wrap_flag is false { s = new session(this, max_session_id, rm, debug); sm.log_data_from_session_table(1,"session with id "+Integer.toString(max_session_id)+" created corresponds to client "+Integer.toString(client_id)+" user "+Integer.toString(user_id)); ste = new session_table_entry(client_id, user_id, s, debug); entries.addElement(ste); max_session_id++; if (max_session_id == Integer.MAX_VALUE) { max_session_id = 0; id_wrap_flag = true; } if (s.process_call_from_client(rf) == s.OK) { reader_writer.writer_exit(); return OK; } else { reader_writer.writer_exit(); return PROCESS_CALL_FROM_CLIENT_FAILED; } } } /** method description: this method will process the function sent by C/C++ marian server. uses the services of class(es): input parameter(s): rf -- the rpc function sent by C/C++ server and need to be processed output parameter(s): none return value: OK -- the function has been processed correctly NULL_RPC_FUNCTION -- the rf is null NULL_PARAMETER -- no parameter is found in rf INVALID_PARAMETER -- the parameter's type of rf is invalid CANNOT_FIND_SESSION -- can not find a session with the specified session id PROCESS_CALL_FROM_C_FAILED -- error happens when session process the function synchronization: reader method */ public int process_call_from_c(rpc_function rf) { int i; parameter p = null; session_table_entry ste = null; int s_id; //session_id if (rf == null) { debug.dumpTrace("Class:session_table Method:process_call_from_c rpc_function is null."); return NULL_RPC_FUNCTION; } p = rf.get_parameter(0); rf.delete_parameter(0); if (p == null) { debug.dumpTrace("Class:session_table Method:process_call_from_c Parameter is null."); return NULL_PARAMETER; } if (!p.get_type().equals("INTEGER")) { debug.dumpTrace("Class:session_table Method:process_call_from_c Invalid parameter type."); return INVALID_PARAMETER; } s_id = ((Integer)p.get_value()).intValue(); reader_writer.reader_enter(); for ( i = 0; i < entries.size(); i++) { ste = (session_table_entry)entries.elementAt(i); if (ste.get_session().get_id() == s_id) { if (ste.get_session().process_call_from_c(rf) == ste.get_session().OK) { reader_writer.reader_exit(); return OK; } else { reader_writer.reader_exit(); return PROCESS_CALL_FROM_C_FAILED; } } } reader_writer.reader_exit(); debug.dumpTrace("Class:session_table Method:process_call_from_c Session with id "+Integer.toString(s_id)+"can't be found."); return CANNOT_FIND_SESSION; } /** method description: this method will process the function sent from one of it's session objects to client (currently webgate). uses the services of class(es): input parameter(s): session_id -- specifies which session send this function rf -- the rpc function sent by the session output parameter(s): none return value: ok -- can not find corresponding client id and user id other -- synchronization: none */ public int process_call_from_session_to_client(int session_id, rpc_function rf) { session_table_process_call_from_session_to_client_thread sthread; sthread = new session_table_process_call_from_session_to_client_thread(this,session_id,rf,debug); sthread.start(); return OK; } /** method description: this method will process the function sent from one of it's session objects to client by thread (currently webgate). uses the services of class(es): input parameter(s): session_id -- specifies which session send this function rf -- the rpc function sent by the session output parameter(s): none return value: OK -- the function has been processed correctly NULL_RPC_FUNCTION -- the rf is null CANNOT_FIND_SESSION -- can not find a session with the specified session id PROCESS_CALL_FROM_SESSION_TABLE_TO_CLIENT_FAILED -- error happens when session manager process the function synchronization: reader method */ public int process_call_from_session_to_client_by_thread(int session_id, rpc_function rf) { int i; session_table_entry ste = null; int client_id; int user_id; parameter p = null; if (rf == null) { debug.dumpTrace("Class:session_table Method:process_call_from_session_to_client_by_thread rpc_function is null."); return NULL_RPC_FUNCTION; } reader_writer.reader_enter(); for ( i = 0; i < entries.size(); i++) { ste = (session_table_entry)entries.elementAt(i); if (ste.get_session().get_id() == session_id) { client_id = ste.get_client_id(); user_id = ste.get_user_id(); p = new parameter(null, "INTEGER", new Integer(user_id), debug); rf.add_parameter(0,p); if (sm.process_call_from_session_table_to_client(client_id,rf) == sm.OK) { reader_writer.reader_exit(); return OK; } else { reader_writer.reader_exit(); return PROCESS_CALL_FROM_SESSION_TABLE_TO_CLIENT_FAILED; } } } reader_writer.reader_exit(); debug.dumpTrace("Class:session_table Method:process_call_from_session_to_client_by_thread Session with id "+Integer.toString(session_id)+" can't be found."); return CANNOT_FIND_SESSION; } /** method description: this method will process the function sent from one of it's session objects to C/C++ marian server. uses the services of class(es): input parameter(s): session_id -- specifies which session send this function rf -- the rpc function sent by the session output parameter(s): none return value: OK -- the function has been processed correctly NULL_RPC_FUNCTION -- rf is null PROCESS_CALL_FROM_SESSION_TABLE_TO_C_FAILED -- error happens when session manager process the function synchronization: none */ public int process_call_from_session_to_c(int session_id, rpc_function rf) { if (rf == null) { debug.dumpTrace("Class:session_table Method:process_call_from_session_to_c rpc_function is null."); return NULL_RPC_FUNCTION; } parameter p = new parameter(null, "INTEGER", new Integer(session_id), debug); rf.add_parameter(0,p); if (sm.process_call_from_session_table_to_c(rf) == sm.OK) { return OK; } else { return PROCESS_CALL_FROM_SESSION_TABLE_TO_C_FAILED; } } /** method description: this method will release all the resource occupied by this object, kill all the threads it created. uses the services of class(es): input parameter(s): condition -- specifies under which condition this method is called output parameter(s): none return value: OK -- the exit is smooth other -- synchronization: writer method */ public int exit(String condition) { int i; session_table_entry ste = null; // the order of the following two statements can't be changed, otherwise will cause a deadlock reader_writer.writer_enter(); sm_thread.stop(); for (i = entries.size()-1; i >=0; i--) { ste = (session_table_entry)entries.elementAt(i); ste.get_session().exit(condition); entries.removeElementAt(i); } sm.log_data_from_session_table(1,"exit under condition: "+condition); reader_writer.writer_exit(); return OK; } /** method description: this method will initialize the data members of this object uses the services of class(es): none input parameter(s): none output parameter(s): none return value: none synchronization: none */ private void init() { sm = null; debug = null; rm = null; sm_thread = null; entries = new Vector(); max_number_sessions = 100; max_session_id = 0; id_wrap_flag = false; return; } /** method description: this method log the data sent from the specified session of this session_table. uses the services of class(es): none input parameter(s): log_level -- specifies the depth of the message generator relative to the session, 1 means it's the session itself session_id -- identifies the session sent the message message -- the message the session want the session_table to log output parameter(s): none return value: OK -- the message has been logged correctly other -- synchronization: none */ int log_data_from_session(int log_level, int session_id, String message) { log_level++; message = "from_session: " + Integer.toString(session_id) + "\n" + message; sm.log_data_from_session_table(log_level,message); return OK; } }