Listing of Source sesspool/SessionPoolingData.javapackage se.entra.phantom.server;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.NoSuchElementException;
import javax.swing.JDesktopPane;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import se.entra.phantom.common.Utilities;
/**
* This class holds the data used by all session
* instances of a pool. It can be extended to provide
* more pool data or functionality. The class is then
* instanciated by the implementor class extending
* from <code>DefaultSessionPoolingHandler</code>
* in the static method <code>createSessionPoolingData</code>.
*/
public class SessionPoolingData implements Runnable
{
//////////////////////////////////////////////////////////
/// The script names, internal functions and "defines" ///
//////////////////////////////////////////////////////////
/**
* The array contains the script names in the index order of the
* table of <code>Element</code> for the scripts. The names are
* case sensitive, as well as the parameters.
*/
public final String [] scriptNames =
{
"start",
"ping",
"check",
"reclaim",
"dispose"
};
/**
* The names of the internal functions.
* The method names are preceeded with "script".
*/
public static final String [] functions =
{
"SCREEN" ,
"CONDITIONS",
"IF" ,
"WHILE" ,
"BREAK" ,
"SET" ,
"SEND" ,
"RESET" ,
"WAIT" ,
"HOSTERROR" ,
"ONERROR" ,
"LOG" ,
"TRACE" ,
"RETURN" ,
"DISPOSE"
};
/**
* The script method indexes: start.
*/
public static final int SCRIPT_START = 0;
/**
* The script method indexes: ping.
*/
public static final int SCRIPT_PING = 1;
/**
* The script method indexes: .
*/
public static final int SCRIPT_CHECK = 2;
/**
* The script method indexes: reclaim.
*/
public static final int SCRIPT_RECLAIM = 3;
/**
* The script method indexes: dispose.
*/
public static final int SCRIPT_DISPOSE = 4;
//////////////////////////////////////////
/// The "final" class instance members ///
//////////////////////////////////////////
/**
* The runtime ID used for this pool.
*/
public final String runtimeID;
/**
* The host ID used (zero is 'A', i.e. (int)(char-'A')).
*/
public final int hostID;
/**
* The minimum amount of sessions in the pool.
*/
public final int minSessions;
/**
* The maximum amount of sessions in the pool.
*/
public final int maxSessions;
/**
* The ping time in seconds (<=0 for none).
*/
public final int pingTime;
/**
* The name of the XML script file.
*/
public final String scriptFile;
/**
* The runtime application data (null if none is used).
*/
public final PhantomRuntime runtime;
/**
* The pool implementing class.
*/
public final Class<?> implClass;
/**
* The child nodes in the XML file data.
*/
public final Node firstChild;
/**
* The JDesktopPane used for the Server GUI (null
* if no GUI is displayed).
*/
public final JDesktopPane desktopPane;
/**
* The pool name.
*/
public final String poolName;
//////////////////////////////
/// Class instance members ///
//////////////////////////////
/**
* This is the vector of all the sessions in the pool.
*
* <p>If is to be modified in some way, use it as a synchronization
* object for thread safety.
*/
private final Vector<DefaultSessionPoolingHandler> sessions = new Vector<DefaultSessionPoolingHandler>();
/**
* The enabled state of the pool (initially disabled).
*/
private boolean isEnabled;
/**
* This flag is true if the pool is already disposed of.
*/
private boolean isDisposed;
/**
* This flag is true if the runtime application must be
* enabled at first start of the pool.
*/
private boolean isEnableAppRequired;
/**
* The registered methods of the script. Each method
* has the argument <code>SessionPoolingScriptData</code>.
*
* <p>The table contains names of the methods as the keys as an instance
* of <code>Method</code> as the element.
*/
private final Hashtable<String,Method> scriptMethods = new Hashtable<String,Method>();
/**
* The different script start nodes.
* null values indicates no script assigned.
*
* <br> 0=start,
* <br> 1=ping,
* <br> 2=check,
* <br> 3=reclaim,
* <br> 4=dispose.
*/
private Element scripts [] = new Element [5];
/**
* The different script maximum times in seconds.
* -1 indicates no maximum time assigned.
*
* <br> 0=start,
* <br> 1=ping,
* <br> 2=check,
* <br> 3=reclaim,
* <br> 4=dispose.
*/
private int maxTimes [] = new int [5];
/**
* The ping checker thread.
*/
private Thread pingThread;
///
/**
* Creates the session pooling data instance.
*
* <p>This method cannot be overridden. For an extending class
* containing extra data members, the <code>final</code> variables
* <code>runtimeID</code>, <code>hostID</code>,
* <code>minSessions</code>, <code>maxSessions</code>,
* <code>pingTime</code>, <code>runtime</code>,
* <code>implClass</code>, <code>firstChild</code>,
* <code>desktopPane</code> and <code>poolName</code>
* must be initialized in the new constructor.
*/
public SessionPoolingData(String runtimeID,
int hostID,
int minSessions,int maxSessions,
int pingTime,
String scriptFile,
PhantomRuntime runtime,
Class<?> implClass,
Node firstChild,
JDesktopPane desktopPane)
{
// Initialize final variables.
this.runtimeID =runtimeID;
this.hostID =hostID;
this.minSessions=minSessions;
this.maxSessions=maxSessions;
this.pingTime =pingTime;
this.scriptFile =scriptFile;
this.runtime =runtime;
this.implClass =implClass;
this.firstChild =firstChild;
this.desktopPane=desktopPane;
// Set the pool name.
poolName="Pool: "+runtimeID+"."+((char)(hostID+'A'));
// Find the scripts.
if ( !getScriptTags() )
logWarning("No script actions found in script file "+scriptFile);
// Register all internal methods.
registerInternalMethods();
// Register all external methods.
registerXMLScriptMethods();
}
///////////////
/// Helpers ///
///////////////
/**
* Adds a trace output for session pooling if client verbose trace is turned on.
*/
protected void trace(String txt)
{
//System.out.println(" >> SESSION-POOLING: "+poolName+": "+txt);
if ( ClientSessionManager.doClientVerboseTrace() )
BinaryTrace.dump(null,"SESSION-POOLING: "+poolName+": "+txt);
}
/**
* Logs an session pooling error event in the event log.
*/
protected void logError(String txt)
{
EventManager.logEvent(EventID.EVENT_E_SessionPooling,poolName+": "+txt);
}
/**
* Logs an session pooling warning event in the event log.
*/
protected void logWarning(String txt)
{
EventManager.logEvent(EventID.EVENT_W_SessionPooling,poolName+": "+txt);
}
/**
* Logs an session pooling informational event in the event log.
*/
protected void logInfo(String txt)
{
EventManager.logEvent(EventID.EVENT_I_SessionPooling,poolName+": "+txt);
}
/////////////////////////
/// Session functions ///
/////////////////////////
/**
* Closes "nicely" or not all the started sessions in
* the pool. The "nicely" flag indicates if the Dispose script
* should run or not.
*
* <p>There is an inactivity timer that will kill the server
* if the method <code>aliveNotification</code> in the
* <code>ClientSessionManager csm</code> parameter that should
* be called at intervals e.g. when a session is about to be
* disposed.
*
* <p>This call blocks the caller thread until all sessions
* are stopped.
*/
@SuppressWarnings("unchecked")
public final void closeAllSessions(ClientSessionManager csm,boolean nicely)
{
synchronized(sessions)
{
if ( isDisposed )
return;
trace("Close all sessions, nicely = "+nicely);
Vector<DefaultSessionPoolingHandler> v=(Vector<DefaultSessionPoolingHandler>)sessions.clone();
for ( Enumeration<DefaultSessionPoolingHandler> e=v.elements(); e.hasMoreElements(); )
{
DefaultSessionPoolingHandler handler=e.nextElement();
if ( nicely && !isDisposed )
handler.startDisposeThread(handler);
else
handler.dispose();
}
// If nicely, wait for all sessions to be closed.
if ( nicely && !isDisposed )
{
if ( sessions.size()>0 )
logInfo("Closing all sessions (running dispose script)");
while ( sessions.size()>0 )
{
try { sessions.wait(); }
catch(InterruptedException e) {}
}
}
trace("Closed all sessions");
}
}
/**
* Disposes "nicely" or not all the started sessions in
* the pool. The "nicely" flag indicates if the Dispose script
* should run or not.
*
* <p>There is an inactivety timer that will kill the server
* if the method <code>aliveNotification</code> in the
* <code>ClientSessionManager csm</code> parameter that should
* be called at intervals e.g. when a session is about to be
* disposed.
*
* <p>This call blocks the caller thread until all sessions
* are stopped.
*/
public final void dispose(ClientSessionManager csm,boolean nicely)
{
synchronized(sessions)
{
if ( isDisposed )
return;
trace("Dispose of pool");
isEnabled=false;
closeAllSessions(csm,nicely);
isDisposed=true;
// Log event.
logInfo("Disposed of all sessions");
// Re-enable the application is required.
if ( isEnableAppRequired )
{
isEnableAppRequired=false;
PhantomRuntime rt=csm.getRuntimeApplication(runtimeID);
if ( rt!=null && !rt.isEnabled() )
{
rt.setEnabled(true);
trace("Enabled application "+runtimeID+" after session pool disposal");
}
}
}
}
/**
* Checks if a session pool is enabled or not.
*/
public boolean isEnabled()
{
return isEnabled;
}
/**
* Changes the enabled state of this pool.
*
* <p>When enabled, this includes starting the minimum
* amount of sessions.
*/
public final void setEnabled(boolean enable)
{
Thread thread;
synchronized(sessions)
{
if ( enable==isEnabled )
return;
isEnabled=enable;
thread=pingThread;
pingThread=null;
}
String s="Pool enabled = "+enable;
trace(s);
logInfo(s);
if ( enable )
{
// Create the minimum amount of sessions and start the ping thread.
createMinimumSessions(true);
if ( pingTime>0 )
{
pingThread=new ServerThread(this,"SPPinger");
pingThread.start();
}
}
else
{
// Re-enable the application is required.
ClientSessionManager csm=ClientSessionManager.getServerAdminInterface();
synchronized(sessions)
{
if ( isEnableAppRequired )
{
isEnableAppRequired=false;
PhantomRuntime rt=csm.getRuntimeApplication(runtimeID);
if ( rt!=null && !rt.isEnabled() )
{
rt.setEnabled(true);
trace("Enabled application "+runtimeID+" due to session pool disabled state");
}
}
}
closeAllSessions(csm,true);
if ( thread!=null )
{
// Wait until thread has exited.
if ( thread.isAlive() )
{
trace("Waiting for Pinger thread to exit");
try { thread.join(); }
catch(InterruptedException e) {}
trace("Pinger thread successfully stopped");
}
}
}
}
/**
* Creates the required minimum amount of sessions in a pool.
*
* <p>During creation of this minimum amount of sessions,
* the runtime application will be disabled.
*
* <p>When the count of started sessions is reached, the
* application will be release. This can be overridden
* by an administrator to activate the application.
*/
private void createMinimumSessions(boolean doDisableRT)
{
try
{
// Check if new sessions are required.
int cc=sessions.size();
if ( !isDisposed && isEnabled && cc<minSessions )
{
// Display progress.
logInfo("Starting new sessions, pool must contain at least "+minSessions+" sessions, current amount "+cc);
// Get the runtime application that should be used,
// this might return null, thus no application is configured.
ClientSessionManager csm=ClientSessionManager.getServerAdminInterface();
if ( doDisableRT && cc==0 )
{
// Only check runtime enabling if no sessions in the
// pool exist.
PhantomRuntime rt=csm.getRuntimeApplication(runtimeID);
if ( rt!=null )
{
// Save state of application, then disable it. When all
// sessions (minimum amount) has been started, the application
// is enabled.
synchronized(sessions)
{
if ( !isEnableAppRequired )
{
isEnableAppRequired=rt.isEnabled();
if ( isEnableAppRequired )
{
rt.setEnabled(false);
String s="Disabled application "+runtimeID+" while minimum amount of sessions are started";
trace(s);
logInfo(s);
}
}
}
}
}
// Create all required instances.
while ( sessions.size()<minSessions && !isDisposed && isEnabled )
createSession(csm,false);
}
}
catch(Exception e)
{
String s="Error creating new sessions in the pool: "+e;
trace(s);
logError(s);
}
}
/**
* Creates a single session.
*
* @return the instance of the session pooling handler found (or created),
* or null if not possible.
*
* @throws InstantiationException for errors when creating the instance.
* @throws IllegalAccessException for errors when creating the instance.
*/
private DefaultSessionPoolingHandler createSession(ClientSessionManager csm,boolean inSameThread) throws InstantiationException, IllegalAccessException
{
trace("Create session - begin");
DefaultSessionPoolingHandler handler=(DefaultSessionPoolingHandler)implClass.newInstance();
sessions.addElement(handler);
ClientConnectionData ccd=csm.createPooledSessionData(poolName);
trace("Create session - initialize");
handler.initialize(this,ccd);
if ( inSameThread )
{
// Do the start in the same thread by calling the run method
// directly.
SessionPoolingScriptRunner runner=new SessionPoolingScriptRunner(handler,SCRIPT_START);
runner.run();
if ( !handler.isValidAndCheckSession() )
{
// Log error.
trace("Failure starting new session");
logError("Failure starting new session");
// Make sure to dispose of it.
if ( !handler.isDisposed() )
handler.dispose();
// In hopeless cases, make sure it's gone in the array.
sessions.removeElement(handler);
handler=null;
}
}
else
startSessionThread(handler,ccd);
trace("Create session - end (OK = "+(handler!=null)+")");
return handler;
}
/**
* Notifies that a session has started correctly.
* This call will enable the application once all sessions
* have started correctly.
*/
public void sessionStarted()
{
synchronized(sessions)
{
if ( isEnabled && !isDisposed )
{
trace("Session has been started");
if ( sessions.size()==minSessions )
{
// Display progress.
String s="Pool reached required amount of minimum sessions "+minSessions;
trace(s);
logInfo(s);
if ( isEnableAppRequired )
{
// Clear enable-app flag.
isEnableAppRequired=false;
// Get the runtime application that should be used,
// this might return null, thus no application is configured.
ClientSessionManager csm=ClientSessionManager.getServerAdminInterface();
PhantomRuntime rt=csm.getRuntimeApplication(runtimeID);
if ( rt!=null && !rt.isEnabled() )
{
rt.setEnabled(true);
s="Enabled application "+runtimeID+" after starting minimum amount of sessions";
logInfo(s);
trace(s);
}
}
}
}
}
}
/**
* Notifies that a session has been disposed of. A waiting dispose-session
* call will be notified when an element has been removed.
*/
public final void sessionDisposed(DefaultSessionPoolingHandler handler)
{
synchronized(sessions)
{
// Remove the element and notify waiting threads.
sessions.removeElement(handler);
sessions.notifyAll();
}
// Check for minimum amount, but don't disable the runtime.
createMinimumSessions(false);
}
/**
* The session pooling ping thread.
*/
@Override
public void run()
{
for ( ;; )
{
// Wait 2 seconds, then check for enabled or disposed.
try { Thread.sleep(2000); }
catch(InterruptedException e) {}
if ( !isEnabled || isDisposed )
break;
// Scan all sessions that could need a ping.
try
{
for ( Enumeration<DefaultSessionPoolingHandler> e=sessions.elements(); e.hasMoreElements(); )
{
DefaultSessionPoolingHandler handler=e.nextElement();
handler.checkPingRequired();
}
}
catch(NoSuchElementException e) {}
}
}
/**
* Starts the execution of the Start Session thread.
*/
private void startSessionThread(DefaultSessionPoolingHandler handler,ClientConnectionData ccd)
{
SessionPoolingScriptRunner runner=new SessionPoolingScriptRunner(handler,SCRIPT_START);
new ServerThread(ccd,runner,"SPStart").start();
}
/**
* Gets all the started sessions. The enumeration
* contains <code>DefaultSessionPoolingHandler</code>
* elements.
*
* @return null if the pool is disposed, otherwise
* and enumeration of <code>DefaultSessionPoolingHandler</code>
* elements.
*/
public Enumeration<DefaultSessionPoolingHandler> getSessions()
{
if ( isDisposed )
return null;
return sessions.elements();
}
/**
* Checks if a session should be reclaimed to the pool.
*/
public boolean isReclaimRequired()
{
if ( isDisposed || !isEnabled )
return false;
return (sessions.size()<=maxSessions);
}
/**
* Checks if there is a session pooling handler that can be used for this
* client session.
*
* @return null if none is found.
*/
public DefaultSessionPoolingHandler grabSessionFromPool()
{
// Disposed or disabled means no available sessions.
if ( isDisposed || !isEnabled )
return null;
// Locate a session that can be used.
try
{
for ( Enumeration<DefaultSessionPoolingHandler> e=sessions.elements(); e.hasMoreElements(); )
{
DefaultSessionPoolingHandler handler=e.nextElement();
if ( handler.isValidAndCheckSession() )
return handler;
}
}
catch(NoSuchElementException e) {}
// No session was available. Start a new one.
String s="No pooled session is available of the "+sessions.size()+" created sessions, starting a new one";
trace(s);
logWarning(s);
try
{
return createSession(ClientSessionManager.getServerAdminInterface(),true);
}
catch(Exception e)
{
s="Error starting new session: "+e;
trace(s);
logError(s);
}
// Return no one found!
return null;
}
/**
* Terminates a single session.
*
* @return false if the session is not found or already disposed,
* true if terminated.
*/
public boolean terminateSession(long connectionID)
{
DefaultSessionPoolingHandler client=null;
synchronized(sessions)
{
try
{
for ( Enumeration<DefaultSessionPoolingHandler> e=sessions.elements(); e.hasMoreElements(); )
{
DefaultSessionPoolingHandler handler=e.nextElement();
if ( handler.getClientConnectionData().getConnectionID()==connectionID )
{
client=handler;
break;
}
}
}
catch(NoSuchElementException e)
{
return false;
}
}
// Check if found.
if ( client==null )
return false;
// Do dispose of this client.
if ( client.isDisposed() )
return false;
client.dispose();
return true;
}
/////////////////////////////////////
/// Method registration & look-up ///
/////////////////////////////////////
/**
* Registers all internal methods in the implementing script class.
*/
protected void registerInternalMethods()
{
trace("Registering internal methods - begin");
for ( int ii=functions.length-1; ii>=0; --ii )
{
String name=functions[ii];
registerScriptMethod(implClass,"script"+name,name);
}
trace("Registering internal methods - end");
}
/**
* This function registers all script methods defined in the XML file.
* Each method has the argument <code>SessionPoolingScriptData</code>.
* The name of the method is case insensitive in the script, but not when
* it is registered (the name must match a method in the declaring class).
*
* <p>If the class registering the method is not an extended class from
* <code>DefaultSessionPoolingHandler</code>, then it must implement the
* interface <code>SessionPoolingScriptMethodInterface</code> that
* defines methods that will be called when the object is instantiated
* for each individual session in the pool.
*
* @return true for successful registration, false otherwise (and
* an event in the server log is created).
*/
protected boolean registerXMLScriptMethods()
{
Node node=firstChild;
while ( node!=null )
{
// Check if Element and "extensions".
if ( node.getNodeName().equals("extensions") && (node instanceof Element) )
{
NodeList nodeList=node.getChildNodes();
for ( int ii=0, cc=nodeList.getLength(); ii<cc; ++ii )
{
Node extensionNode=nodeList.item(ii);
if ( extensionNode.getNodeName().equals("function") && (extensionNode instanceof Element) )
{
// Find the script name that matches, skip if already
// defined.
Element element=(Element)extensionNode;
String name =element.getAttribute("name" );
String clazz =element.getAttribute("class" );
String method=element.getAttribute("method");
try
{
Class<?> c;
if ( clazz.length()==0 )
{
trace("Registering external function "+name+", method "+method);
c=implClass;
clazz=implClass.getName();
}
else
{
trace("Registering external function "+name+" in class "+clazz+" method "+method);
c=Class.forName(clazz);
}
registerScriptMethod(c,method,name);
}
catch(Throwable e)
{
String s="Register method "+method+" failure: class "+clazz+", method name "+method+", function name "+name+": "+e;
trace(s);
logError(s);
continue;
}
}
}
}
// Try next one.
node=node.getNextSibling();
}
return true;
}
/**
* Gets the action scripts in a script file. These are the
* "action name=nnn" tags under the "actions" main tag.
*
* @return true if all scripts are successfully registered.
*/
protected boolean getScriptTags()
{
// First locate the "actions" tag.
Node node=firstChild;
while ( node!=null )
{
// Check if Element and "actions".
if ( node.getNodeName().equals("actions") && (node instanceof Element) )
{
// Found the actions. Now find the "action" tags that
// belongs to the "actions" tag.
int found=0;
NodeList nodeList=node.getChildNodes();
for ( int ii=0, cc=nodeList.getLength(); ii<cc; ++ii )
{
Node actionNode=nodeList.item(ii);
if ( actionNode.getNodeName().equals("action") && (actionNode instanceof Element) )
{
// Find the script name that matches, skip if already
// defined.
Element element=(Element)actionNode;
String name=element.getAttribute("name");
for ( int jj=scriptNames.length-1; jj>=0; --jj )
{
if ( name.equalsIgnoreCase(scriptNames[jj]) )
{
// Already taken? Skip it...
if ( scripts[jj]!=null )
{
trace("Action "+name+" already assigned, skipping");
break;
}
// Get the maximum time.
int mt=-1;
try { mt=Integer.parseInt(element.getAttribute("maxtime")); }
catch(Exception e) {}
// Check if there is a script element child node. If so,
// use it...
NodeList elementScripts=element.getElementsByTagName("script");
if ( elementScripts.getLength()>0 )
{
Node scriptStart=elementScripts.item(0);
if ( scriptStart instanceof Element )
{
// Save script element node.
trace("Action "+name+" script "+scriptStart+" maxtime = "+mt);
scripts[jj]=(Element)scriptStart;
maxTimes[jj]=mt;
// Set script found...
++found;
break;
}
}
// No script found.
trace("Action "+name+" didn't have a valid <script>");
break;
}
}
}
}
// Return OK if all "action" tags are found.
return (found==scriptNames.length);
}
node=node.getNextSibling();
}
// Log event and return failure...
logError("Script "+scriptFile+": <actions> tag not found");
trace("Script "+scriptFile+": <actions> tag not found");
return false;
}
/**
* Gets a method from a script tag in the global pool data.
* The name of the method is case insensitive.
*
* @return null if no method is found, otherwise the
* <code>Method</code> instance.
*/
public Method getScriptTagMethod(String name)
{
return scriptMethods.get(name.toUpperCase());
}
////////////////////////
/// Script execution ///
////////////////////////
/**
* Executes the specified script index (SCRIPT_nnn value).
*
* <p>If the script is not defined in the XML file, true
* is returned.
*
* @return true or false depending on the script.
*
* @throws SessionPoolingDisposed if the session is disposed.
*/
public boolean executeScript(DefaultSessionPoolingHandler handler,int scriptIndex) throws SessionPoolingDisposed
{
// Log start event.
String name=scriptNames[scriptIndex];
trace("Script "+name+" started");
boolean returnCode=true;
try
{
Node parent=scripts[scriptIndex];
if ( parent!=null )
{
// Set the maximum execution time.
long maxTime=maxTimes[scriptIndex]*1000L;
if ( maxTime>0 )
maxTime+=System.currentTimeMillis();
// Execute the script.
SessionPoolingScriptData data=new SessionPoolingScriptData(handler,name,parent,maxTime);
try
{
data.currentElement=data.getFirstElement(parent);
data.executeRemainingElements();
}
catch(SessionPoolingScriptBreak e) {}
catch(SessionPoolingScriptReturn e) {}
// Set return code.
returnCode=data.returnCode;
}
}
catch(SessionPoolingScriptHostError e)
{
// Host error.
trace("Script "+name+": unhandled host error (no onerror tag)");
handler.disposeNow();
}
catch(SessionPoolingDisposed e)
{
// Log disposed event.
trace("Script "+name+" disposed");
handler.disposeNow();
}
catch(Throwable e)
{
// Log internal error event.
trace("Script "+name+" internal error: "+e+":\n"+Utilities.getStackTrace(e));
return false;
}
// Log stopped event.
trace("Script "+name+" stopped, return code "+returnCode);
return returnCode;
}
/**
* This function registers a script method in the table. Each method
* has the argument <code>SessionPoolingScriptData</code>.
* The name of the method is case insensitive in the script, but not when
* it is registered (the name must match a method in the declaring class).
*
* <p>If the class registering the method is not an extended class from
* <code>DefaultSessionPoolingHandler</code>, then it must implement the
* interface <code>SessionPoolingScriptMethodInterface</code> that
* defines methods that will be called when the object is instantiated
* for each individual session in the pool.
*
* @return true for successful registration, false otherwise (and
* an event in the server log is created).
*/
public boolean registerScriptMethod(Class<?> clazz,String methodName,String scriptName)
{
try
{
Class<?> [] params={ SessionPoolingScriptData.class };
Method method=clazz.getDeclaredMethod(methodName,params);
scriptName=scriptName.toUpperCase();
if ( scriptMethods.get(scriptName)!=null )
{
String s="Register duplicate method "+methodName+": class "+clazz.getName()+", function name "+scriptName;
trace(s);
logError(s);
return false;
}
scriptMethods.put(scriptName,method);
return true;
}
catch(Exception e)
{
String s="Register method "+methodName+" failure: class "+clazz.getName()+", function name "+scriptName+": "+e;
trace(s);
logError(s);
return false;
}
}
}