alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Tomcat example source code file (JvmRouteBinderValve.java)

This example Tomcat source code file (JvmRouteBinderValve.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Tomcat tags/keywords

catalinacluster, catalinacluster, deltasession, engine, host, http, io, ioexception, lifecycleexception, lifecycleexception, manager, request, response, response, servlet, session, string, string

The Tomcat JvmRouteBinderValve.java source code

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.ha.session;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.ha.ClusterValve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.util.LifecycleSupport;
import org.apache.catalina.util.StringManager;
import org.apache.catalina.valves.ValveBase;

/**
 * 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:<br/>
 * <pre>
 *  <Cluster>
 *  <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />  
 *  </Cluster>
 * </pre>
 * <br />
 * Before 5.5.10 as Host element:<br/>
 * <pre>
 *  <Hostr>
 *  <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />  
 *  </Hostr>
 * </pre>
 * 
 * Trick:<br/>
 * 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) $
 */
public class JvmRouteBinderValve extends ValveBase implements ClusterValve, Lifecycle {

    /*--Static Variables----------------------------------------*/
    public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
            .getLog(JvmRouteBinderValve.class);

    /**
     * The descriptive information about this implementation.
     */
    protected static final String info = "org.apache.catalina.ha.session.JvmRouteBinderValve/1.2";

    /*--Instance Variables--------------------------------------*/

    /**
     * the cluster
     */
    protected CatalinaCluster cluster;

    /**
     * The string manager for this package.
     */
    protected StringManager sm = StringManager.getManager(Constants.Package);

    /**
     * Has this component been started yet?
     */
    protected boolean started = false;

    /**
     * enabled this component
     */
    protected boolean enabled = true;

    /**
     * number of session that no at this tomcat instanz hosted
     */
    protected long numberOfSessions = 0;

    protected String sessionIdAttribute = "org.apache.catalina.ha.session.JvmRouteOrignalSessionID";

    /**
     * The lifecycle event support for this component.
     */
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);

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

    /**
     * Return descriptive information about this implementation.
     */
    public String getInfo() {

        return (info);

    }

    /**
     * set session id attribute to failed node for request.
     * 
     * @return Returns the sessionIdAttribute.
     */
    public String getSessionIdAttribute() {
        return sessionIdAttribute;
    }

    /**
     * get name of failed reqeust session attribute
     * 
     * @param sessionIdAttribute
     *            The sessionIdAttribute to set.
     */
    public void setSessionIdAttribute(String sessionIdAttribute) {
        this.sessionIdAttribute = sessionIdAttribute;
    }

    /**
     * @return Returns the number of migrated sessions.
     */
    public long getNumberOfSessions() {
        return numberOfSessions;
    }

    /**
     * @return Returns the enabled.
     */
    public boolean getEnabled() {
        return enabled;
    }

    /**
     * @param enabled
     *            The enabled to set.
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    /**
     * 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
     */
    public void invoke(Request request, Response response) throws IOException,
            ServletException {

         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);
    }

    /**
     * handle possible session turn over.
     * 
     * @see JvmRouteBinderValve#handleJvmRoute(Request, Response, String, String)
     * @param request current request
     * @param response current response
     */
    protected void handlePossibleTurnover(Request request, Response 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)));
            }
        }
    }

    /**
     * get jvmroute from engine
     * 
     * @param request current request
     * @return return jvmRoute from ManagerBase or null
     */
    protected String getLocalJvmRoute(Request request) {
        Manager manager = getManager(request);
        if(manager instanceof ManagerBase)
            return ((ManagerBase) manager).getJvmRoute();
        return null ;
    }

    /**
     * get Cluster DeltaManager
     * 
     * @param request current request
     * @return manager or null
     */
    protected Manager getManager(Request request) {
        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;
    }

    /**
     * @return Returns the cluster.
     */
    public CatalinaCluster getCluster() {
        return cluster;
    }
    
    /**
     * @param cluster The cluster to set.
     */
    public void setCluster(CatalinaCluster cluster) {
        this.cluster = cluster;
    }
    
    /**
     * 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
     */
    protected void handleJvmRoute(
            Request request, Response response,String sessionId, String localJvmRoute) {
        // 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));
                }
            }
        }
    }

    /**
     * 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
     */
    protected void changeSessionID(Request request,
            Response response, String sessionId, String newSessionID, Session catalinaSession) {
        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));
        }
    }

    /**
     * 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
     */
    protected void sendSessionIDClusterBackup(ClusterManager manager,Request request,String sessionId,
            String newSessionID) {
        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);
    }

    /**
     * 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
     */
    protected void setNewSessionCookie(Request request,
                                       Response response, String sessionId) {
        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);
            }
        }
    }

    // ------------------------------------------------------ Lifecycle Methods

    /**
     * Add a lifecycle event listener to this component.
     * 
     * @param listener
     *            The listener to add
     */
    public void addLifecycleListener(LifecycleListener listener) {

        lifecycle.addLifecycleListener(listener);

    }

    /**
     * Get the lifecycle listeners associated with this lifecycle. If this
     * Lifecycle has no listeners registered, a zero-length array is returned.
     */
    public LifecycleListener[] findLifecycleListeners() {

        return lifecycle.findLifecycleListeners();

    }

    /**
     * Remove a lifecycle event listener from this component.
     * 
     * @param listener
     *            The listener to add
     */
    public void removeLifecycleListener(LifecycleListener listener) {

        lifecycle.removeLifecycleListener(listener);

    }

    /**
     * Prepare for the beginning of active use of the public methods of this
     * component. This method should be called after <code>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
     */
    public void start() throws LifecycleException {

        // 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"));

    }

    /**
     * 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
     */
    public void stop() throws LifecycleException {

        // 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"));

    }

}

Other Tomcat examples (source code examples)

Here is a short list of links related to this Tomcat JvmRouteBinderValve.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.