FileDocCategorySizeDatePackage
JvmRouteBinderValve.javaAPI DocApache Tomcat 6.0.1420302Fri Jul 20 04:20:32 BST 2007org.apache.catalina.ha.session

JvmRouteBinderValve

public class JvmRouteBinderValve extends org.apache.catalina.valves.ValveBase implements org.apache.catalina.Lifecycle, org.apache.catalina.ha.ClusterValve
Valve to handle Tomcat jvmRoute takeover using mod_jk module after node failure. After a node crashed the next request going to other cluster node. Now the answering from apache is slower ( make some error handshaking. Very bad with apache at my windows.). We rewrite now the cookie jsessionid information to the backup cluster node. After the next response all client request goes direct to the backup node. The change sessionid send also to all other cluster nodes. Well, now the session stickyness work directly to the backup node and traffic don't go back too restarted cluster nodes! At all cluster node you must configure the as ClusterListener since 5.5.10 {@link org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener JvmRouteSessionIDBinderListener} or before with org.apache.catalina.ha.session.JvmRouteSessionIDBinderListenerLifecycle. Add this Valve to your host definition at conf/server.xml . Since 5.5.10 as direct cluster valve:
<Cluster>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
</Cluster>

Before 5.5.10 as Host element:
<Hostr>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
</Hostr>
Trick:
You can enable this mod_jk turnover mode via JMX before you drop a node to all backup nodes! Set enable true on all JvmRouteBinderValve backups, disable worker at mod_jk and then drop node and restart it! Then enable mod_jk Worker and disable JvmRouteBinderValves again. This use case means that only requested session are migrated.
author
Peter Rossbach
version
$Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $

Fields Summary
public static org.apache.juli.logging.Log
log
protected static final String
info
The descriptive information about this implementation.
protected org.apache.catalina.ha.CatalinaCluster
cluster
the cluster
protected org.apache.catalina.util.StringManager
sm
The string manager for this package.
protected boolean
started
Has this component been started yet?
protected boolean
enabled
enabled this component
protected long
numberOfSessions
number of session that no at this tomcat instanz hosted
protected String
sessionIdAttribute
protected org.apache.catalina.util.LifecycleSupport
lifecycle
The lifecycle event support for this component.
Constructors Summary
Methods Summary
public voidaddLifecycleListener(org.apache.catalina.LifecycleListener listener)
Add a lifecycle event listener to this component.

param
listener The listener to add


        lifecycle.addLifecycleListener(listener);

    
protected voidchangeSessionID(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, java.lang.String sessionId, java.lang.String newSessionID, org.apache.catalina.Session catalinaSession)
change session id and send to all cluster nodes

param
request current request
param
response current response
param
sessionId original session id
param
newSessionID new session id for node migration
param
catalinaSession current session with original session id

        lifecycle.fireLifecycleEvent("Before session migration",
                catalinaSession);
        request.setRequestedSessionId(newSessionID);
        catalinaSession.setId(newSessionID);
        if (catalinaSession instanceof DeltaSession)
            ((DeltaSession) catalinaSession).resetDeltaRequest();
        if(request.isRequestedSessionIdFromCookie()) setNewSessionCookie(request, response,newSessionID);
        // set orginal sessionid at request, to allow application detect the
        // change
        if (sessionIdAttribute != null && !"".equals(sessionIdAttribute)) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("jvmRoute.set.orignalsessionid",sessionIdAttribute,sessionId));
            }
            request.setAttribute(sessionIdAttribute, sessionId);
        }
        // now sending the change to all other clusternode!
        ClusterManager manager = (ClusterManager)catalinaSession.getManager();
        sendSessionIDClusterBackup(manager,request,sessionId, newSessionID);
        lifecycle
                .fireLifecycleEvent("After session migration", catalinaSession);
        if (log.isDebugEnabled()) {
            log.debug(sm.getString("jvmRoute.changeSession", sessionId,
                    newSessionID));
        }
    
public org.apache.catalina.LifecycleListener[]findLifecycleListeners()
Get the lifecycle listeners associated with this lifecycle. If this Lifecycle has no listeners registered, a zero-length array is returned.


        return lifecycle.findLifecycleListeners();

    
public org.apache.catalina.ha.CatalinaClustergetCluster()

return
Returns the cluster.

        return cluster;
    
public booleangetEnabled()

return
Returns the enabled.

        return enabled;
    
public java.lang.StringgetInfo()
Return descriptive information about this implementation.


    /*--Logic---------------------------------------------------*/

               
       

        return (info);

    
protected java.lang.StringgetLocalJvmRoute(org.apache.catalina.connector.Request request)
get jvmroute from engine

param
request current request
return
return jvmRoute from ManagerBase or null

        Manager manager = getManager(request);
        if(manager instanceof ManagerBase)
            return ((ManagerBase) manager).getJvmRoute();
        return null ;
    
protected org.apache.catalina.ManagergetManager(org.apache.catalina.connector.Request request)
get Cluster DeltaManager

param
request current request
return
manager or null

        Manager manager = request.getContext().getManager();
        if (log.isDebugEnabled()) {
            if(manager != null)
                log.debug(sm.getString("jvmRoute.foundManager", manager,  request.getContext().getName()));
            else 
                log.debug(sm.getString("jvmRoute.notFoundManager", manager,  request.getContext().getName()));
        }
        return manager;
    
public longgetNumberOfSessions()

return
Returns the number of migrated sessions.

        return numberOfSessions;
    
public java.lang.StringgetSessionIdAttribute()
set session id attribute to failed node for request.

return
Returns the sessionIdAttribute.

        return sessionIdAttribute;
    
protected voidhandleJvmRoute(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, java.lang.String sessionId, java.lang.String localJvmRoute)
Handle jvmRoute stickyness after tomcat instance failed. After this correction a new Cookie send to client with new jvmRoute and the SessionID change propage to the other cluster nodes.

param
request current request
param
response Tomcat Response
param
sessionId request SessionID from Cookie
param
localJvmRoute local jvmRoute

        // get requested jvmRoute.
        String requestJvmRoute = null;
        int index = sessionId.indexOf(".");
        if (index > 0) {
            requestJvmRoute = sessionId
                    .substring(index + 1, sessionId.length());
        }
        if (requestJvmRoute != null && !requestJvmRoute.equals(localJvmRoute)) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("jvmRoute.failover", requestJvmRoute,
                        localJvmRoute, sessionId));
            }
            // OK - turnover the session ?
            String newSessionID = sessionId.substring(0, index) + "."
                    + localJvmRoute;
            Session catalinaSession = null;
            try {
                catalinaSession = getManager(request).findSession(sessionId);
            } catch (IOException e) {
                // Hups!
            }
            if (catalinaSession != null) {
                changeSessionID(request, response, sessionId, newSessionID,
                        catalinaSession);
                numberOfSessions++;
            } else {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("jvmRoute.cannotFindSession",
                            sessionId));
                }
            }
        }
    
protected voidhandlePossibleTurnover(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response)
handle possible session turn over.

see
JvmRouteBinderValve#handleJvmRoute(Request, Response, String, String)
param
request current request
param
response current response

        Session session = request.getSessionInternal(false);
        if (session != null) {
            long t1 = System.currentTimeMillis();
            String jvmRoute = getLocalJvmRoute(request);
            if (jvmRoute == null) {
                if (log.isDebugEnabled())
                    log.debug(sm.getString("jvmRoute.missingJvmRouteAttribute"));
                return;
            }
            handleJvmRoute( request, response,session.getIdInternal(), jvmRoute);
            if (log.isDebugEnabled()) {
                long t2 = System.currentTimeMillis();
                long time = t2 - t1;
                log.debug(sm.getString("jvmRoute.turnoverInfo", new Long(time)));
            }
        }
    
public voidinvoke(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response)
Detect possible the JVMRoute change at cluster backup node..

param
request tomcat request being processed
param
response tomcat response being processed
exception
IOException if an input/output error has occurred
exception
ServletException if a servlet error has occurred


         if (getEnabled() 
             && getCluster() != null
             && request.getContext() != null
             && request.getContext().getDistributable() ) {
             // valve cluster can access manager - other cluster handle turnover 
             // at host level - hopefully!
             Manager manager = request.getContext().getManager();
             if (manager != null && manager instanceof ClusterManager
                     && getCluster().getManager(((ClusterManager)manager).getName()) != null)
                 handlePossibleTurnover(request, response);
        }
        // Pass this request on to the next valve in our pipeline
        getNext().invoke(request, response);
    
public voidremoveLifecycleListener(org.apache.catalina.LifecycleListener listener)
Remove a lifecycle event listener from this component.

param
listener The listener to add


        lifecycle.removeLifecycleListener(listener);

    
protected voidsendSessionIDClusterBackup(org.apache.catalina.ha.ClusterManager manager, org.apache.catalina.connector.Request request, java.lang.String sessionId, java.lang.String newSessionID)
Send the changed Sessionid to all clusternodes.

see
JvmRouteSessionIDBinderListener#messageReceived(ClusterMessage)
param
manager ClusterManager
param
sessionId current failed sessionid
param
newSessionID new session id, bind to the new cluster node

        SessionIDMessage msg = new SessionIDMessage();
        msg.setOrignalSessionID(sessionId);
        msg.setBackupSessionID(newSessionID);
        Context context = request.getContext();
        msg.setContextPath(context.getPath());
        msg.setHost(context.getParent().getName());
        if(manager.doDomainReplication())
            cluster.sendClusterDomain(msg);
        else
            cluster.send(msg);
    
public voidsetCluster(org.apache.catalina.ha.CatalinaCluster cluster)

param
cluster The cluster to set.

        this.cluster = cluster;
    
public voidsetEnabled(boolean enabled)

param
enabled The enabled to set.

        this.enabled = enabled;
    
protected voidsetNewSessionCookie(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response, java.lang.String sessionId)
Sets a new cookie for the given session id and response and see {@link org.apache.catalina.connector.Request#configureSessionCookie(javax.servlet.http.Cookie)}

param
request current request
param
response Tomcat Response
param
sessionId The session id

        if (response != null) {
            Context context = request.getContext();
            if (context.getCookies()) {
                // set a new session cookie
                Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,
                        sessionId);
                newCookie.setMaxAge(-1);
                String contextPath = null;
                if (!response.getConnector().getEmptySessionPath()
                        && (context != null)) {
                    contextPath = context.getEncodedPath();
                }
                if ((contextPath != null) && (contextPath.length() > 0)) {
                    newCookie.setPath(contextPath);
                } else {
                    newCookie.setPath("/");
                }
                if (request.isSecure()) {
                    newCookie.setSecure(true);
                }
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("jvmRoute.newSessionCookie",
                            sessionId, Globals.SESSION_COOKIE_NAME, newCookie
                                    .getPath(), new Boolean(newCookie
                                    .getSecure())));
                }
                response.addCookie(newCookie);
            }
        }
    
public voidsetSessionIdAttribute(java.lang.String sessionIdAttribute)
get name of failed reqeust session attribute

param
sessionIdAttribute The sessionIdAttribute to set.

        this.sessionIdAttribute = sessionIdAttribute;
    
public voidstart()
Prepare for the beginning of active use of the public methods of this component. This method should be called after configure(), and before any of the public methods of the component are utilized.

exception
LifecycleException if this component detects a fatal error that prevents this component from being used


        // Validate and update our current component state
        if (started)
            throw new LifecycleException(sm
                    .getString("jvmRoute.valve.alreadyStarted"));
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;
        if (cluster == null) {
            Container hostContainer = getContainer();
            // compatibility with JvmRouteBinderValve version 1.1
            // ( setup at context.xml or context.xml.default )
            if (!(hostContainer instanceof Host)) {
                if (log.isWarnEnabled())
                    log.warn(sm.getString("jvmRoute.configure.warn"));
                hostContainer = hostContainer.getParent();
            }
            if (hostContainer instanceof Host
                    && ((Host) hostContainer).getCluster() != null) {
                cluster = (CatalinaCluster) ((Host) hostContainer).getCluster();
            } else {
                Container engine = hostContainer.getParent() ;
                if (engine instanceof Engine
                        && ((Engine) engine).getCluster() != null) {
                    cluster = (CatalinaCluster) ((Engine) engine).getCluster();
                }
            }
        }
        if (cluster == null) {
            throw new RuntimeException("No clustering support at container "
                    + container.getName());
        }
        
        if (log.isInfoEnabled())
            log.info(sm.getString("jvmRoute.valve.started"));

    
public voidstop()
Gracefully terminate the active use of the public methods of this component. This method should be the last one called on a given instance of this component.

exception
LifecycleException if this component detects a fatal error that needs to be reported


        // Validate and update our current component state
        if (!started)
            throw new LifecycleException(sm
                    .getString("jvmRoute.valve.notStarted"));
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        cluster = null;
        numberOfSessions = 0;
        if (log.isInfoEnabled())
            log.info(sm.getString("jvmRoute.valve.stopped"));