FileDocCategorySizeDatePackage
ReplicationValve.javaAPI DocApache Tomcat 6.0.1423046Fri Jul 20 04:20:32 BST 2007org.apache.catalina.ha.tcp

ReplicationValve

public class ReplicationValve extends org.apache.catalina.valves.ValveBase implements org.apache.catalina.ha.ClusterValve

Implementation of a Valve that logs interesting contents from the specified Request (before processing) and the corresponding Response (after processing). It is especially useful in debugging problems related to headers and cookies.

This Valve may be attached to any Container, depending on the granularity of the logging you wish to perform.

primaryIndicator=true, then the request attribute org.apache.catalina.ha.tcp.isPrimarySession. is set true, when request processing is at sessions primary node.

author
Craig R. McClanahan
author
Filip Hanik
author
Peter Rossbach
version
$Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $

Fields Summary
private static org.apache.juli.logging.Log
log
private static final String
info
The descriptive information related to this implementation.
protected static org.apache.catalina.util.StringManager
sm
The StringManager for this package.
private org.apache.catalina.ha.CatalinaCluster
cluster
protected Pattern[]
reqFilters
holds file endings to not call for like images and others
protected String
filter
Orginal filter
protected ThreadLocal
crossContextSessions
crossContext session container
protected boolean
doProcessingStats
doProcessingStats (default = off)
protected long
totalRequestTime
protected long
totalSendTime
protected long
nrOfRequests
protected long
lastSendTime
protected long
nrOfFilterRequests
protected long
nrOfSendRequests
protected long
nrOfCrossContextSendRequests
protected boolean
primaryIndicator
must primary change indicator set
protected String
primaryIndicatorName
Name of primary change indicator as request attribute
Constructors Summary
public ReplicationValve()

   
    // ------------------------------------------------------------- Properties

      
    
Methods Summary
protected voidcreatePrimaryIndicator(org.apache.catalina.connector.Request request)
Mark Request that processed at primary node with attribute primaryIndicatorName

param
request
throws
IOException

        String id = request.getRequestedSessionId();
        if ((id != null) && (id.length() > 0)) {
            Manager manager = request.getContext().getManager();
            Session session = manager.findSession(id);
            if (session instanceof ClusterSession) {
                ClusterSession cses = (ClusterSession) session;
                if (cses != null) {
                    if (log.isDebugEnabled())
                        log.debug(sm.getString(
                                "ReplicationValve.session.indicator", request.getContext().getName(),id,
                                primaryIndicatorName, cses.isPrimarySession()));
                    request.setAttribute(primaryIndicatorName, cses.isPrimarySession()?Boolean.TRUE:Boolean.FALSE);
                }
            } else {
                if (log.isDebugEnabled()) {
                    if (session != null) {
                        log.debug(sm.getString(
                                "ReplicationValve.session.found", request.getContext().getName(),id));
                    } else {
                        log.debug(sm.getString(
                                "ReplicationValve.session.invalid", request.getContext().getName(),id));
                    }
                }
            }
        }
    
public booleandoStatistics()
Calc processing stats

        return doProcessingStats;
    
public org.apache.catalina.ha.CatalinaClustergetCluster()

return
Returns the cluster.

        return cluster;
    
public java.lang.StringgetFilter()

return
Returns the filter

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


        return (info);

    
public longgetLastSendTime()

return
Returns the lastSendTime.

        return lastSendTime;
    
public longgetNrOfCrossContextSendRequests()

return
Returns the nrOfCrossContextSendRequests.

        return nrOfCrossContextSendRequests;
    
public longgetNrOfFilterRequests()

return
Returns the nrOfFilterRequests.

        return nrOfFilterRequests;
    
public longgetNrOfRequests()

return
Returns the nrOfRequests.

        return nrOfRequests;
    
public longgetNrOfSendRequests()

return
Returns the nrOfSendRequests.

        return nrOfSendRequests;
    
public java.lang.StringgetPrimaryIndicatorName()

return
Returns the primaryIndicatorName.

        return primaryIndicatorName;
    
protected java.util.regex.Pattern[]getReqFilters()

return
Returns the reqFilters.

        return reqFilters;
    
public longgetTotalRequestTime()

return
Returns the totalRequestTime.

        return totalRequestTime;
    
public longgetTotalSendTime()

return
Returns the totalSendTime.

        return totalSendTime;
    
public voidinvoke(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response)
Log the interesting request parameters, invoke the next Valve in the sequence, and log the interesting response parameters.

param
request The servlet request to be processed
param
response The servlet response to be created
exception
IOException if an input/output error occurs
exception
ServletException if a servlet error occurs

        long totalstart = 0;

        //this happens before the request
        if(doStatistics()) {
            totalstart = System.currentTimeMillis();
        }
        if (primaryIndicator) {
            createPrimaryIndicator(request) ;
        }
        Context context = request.getContext();
        boolean isCrossContext = context != null
                && context instanceof StandardContext
                && ((StandardContext) context).getCrossContext();
        try {
            if(isCrossContext) {
                if(log.isDebugEnabled())
                    log.debug(sm.getString("ReplicationValve.crossContext.add"));
                //FIXME add Pool of Arraylists
                crossContextSessions.set(new ArrayList());
            }
            getNext().invoke(request, response);
            Manager manager = request.getContext().getManager();
            if (manager != null && manager instanceof ClusterManager) {
                ClusterManager clusterManager = (ClusterManager) manager;
                CatalinaCluster containerCluster = (CatalinaCluster) getContainer().getCluster();
                if (containerCluster == null) {
                    if (log.isWarnEnabled())
                        log.warn(sm.getString("ReplicationValve.nocluster"));
                    return;
                }
                // valve cluster can access manager - other cluster handle replication 
                // at host level - hopefully!
                if(containerCluster.getManager(clusterManager.getName()) == null)
                    return ;
                if(containerCluster.hasMembers()) {
                    sendReplicationMessage(request, totalstart, isCrossContext, clusterManager, containerCluster);
                } else {
                    resetReplicationRequest(request,isCrossContext);
                }        
            }
        } finally {
            // Array must be remove: Current master request send endAccess at recycle. 
            // Don't register this request session again!
            if(isCrossContext) {
                if(log.isDebugEnabled())
                    log.debug(sm.getString("ReplicationValve.crossContext.remove"));
                // crossContextSessions.remove() only exist at Java 5
                // register ArrayList at a pool
                crossContextSessions.set(null);
            }
        }
    
public booleanisPrimaryIndicator()

return
Returns the primaryIndicator.

        return primaryIndicator;
    
protected booleanisRequestWithoutSessionChange(java.lang.String uri)
is request without possible session change

param
uri The request uri
return
True if no session change


        boolean filterfound = false;

        for (int i = 0; (i < reqFilters.length) && (!filterfound); i++) {
            java.util.regex.Matcher matcher = reqFilters[i].matcher(uri);
            filterfound = matcher.matches();
        }
        return filterfound;
    
public voidregisterReplicationSession(org.apache.catalina.ha.session.DeltaSession session)
Register all cross context sessions inside endAccess. Use a list with contains check, that the Portlet API can include a lot of fragments from same or different applications with session changes.

param
session cross context session

        List sessions = (List)crossContextSessions.get();
        if(sessions != null) {
            if(!sessions.contains(session)) {
                if(log.isDebugEnabled())
                    log.debug(sm.getString("ReplicationValve.crossContext.registerSession",
                        session.getIdInternal(),
                        session.getManager().getContainer().getName()));
                sessions.add(session);
            }
        }
    
protected voidresetDeltaRequest(org.apache.catalina.Session session)
Reset DeltaRequest from session

param
session HttpSession from current request or cross context session

        if(log.isDebugEnabled()) {
            log.debug(sm.getString("ReplicationValve.resetDeltaRequest" , 
                session.getManager().getContainer().getName() ));
        }
        ((DeltaSession)session).resetDeltaRequest();
    
protected voidresetReplicationRequest(org.apache.catalina.connector.Request request, boolean isCrossContext)
Fix memory leak for long sessions with many changes, when no backup member exists!

param
request current request after responce is generated
param
isCrossContext check crosscontext threadlocal

        Session contextSession = request.getSessionInternal(false);
        if(contextSession != null & contextSession instanceof DeltaSession){
            resetDeltaRequest(contextSession);
            ((DeltaSession)contextSession).setPrimarySession(true);
        }
        if(isCrossContext) {
            Object sessions = crossContextSessions.get();
            if(sessions != null && sessions instanceof List
               && ((List)sessions).size() >0) {
                Iterator iter = ((List)sessions).iterator();
                for(; iter.hasNext() ;) {          
                    Session session = (Session)iter.next();
                    resetDeltaRequest(session);
                    if(session instanceof DeltaSession)
                        ((DeltaSession)contextSession).setPrimarySession(true);

                }
            }
        }                     
    
public voidresetStatistics()
reset the active statitics

        totalRequestTime = 0 ;
        totalSendTime = 0 ;
        lastSendTime = 0 ;
        nrOfFilterRequests = 0 ;
        nrOfRequests = 0 ;
        nrOfSendRequests = 0;
        nrOfCrossContextSendRequests = 0;
    
protected voidsend(org.apache.catalina.ha.ClusterManager manager, org.apache.catalina.ha.CatalinaCluster cluster, java.lang.String sessionId)
send manager requestCompleted message to cluster

param
manager SessionManager
param
cluster replication cluster
param
sessionId sessionid from the manager
see
DeltaManager#requestCompleted(String)
see
SimpleTcpCluster#send(ClusterMessage)

        ClusterMessage msg = manager.requestCompleted(sessionId);
        if (msg != null) {
            if(manager.doDomainReplication()) {
                cluster.sendClusterDomain(msg);
            } else {
                cluster.send(msg);
            }
            if(doStatistics())
                nrOfSendRequests++;
        }
    
protected voidsendCrossContextSession(org.apache.catalina.ha.CatalinaCluster containerCluster)
Send all changed cross context sessions to backups

param
containerCluster

        Object sessions = crossContextSessions.get();
        if(sessions != null && sessions instanceof List
                && ((List)sessions).size() >0) {
            for(Iterator iter = ((List)sessions).iterator(); iter.hasNext() ;) {          
                Session session = (Session)iter.next();
                if(log.isDebugEnabled())
                    log.debug(sm.getString("ReplicationValve.crossContext.sendDelta",  
                            session.getManager().getContainer().getName() ));
                sendMessage(session,(ClusterManager)session.getManager(),containerCluster);
                if(doStatistics()) {
                    nrOfCrossContextSendRequests++;
                }
            }
        }
    
protected voidsendInvalidSessions(org.apache.catalina.ha.ClusterManager manager, org.apache.catalina.ha.CatalinaCluster cluster)
check for session invalidations

param
manager
param
cluster

        String[] invalidIds=manager.getInvalidatedSessions();
        if ( invalidIds.length > 0 ) {
            for ( int i=0;i<invalidIds.length; i++ ) {
                try {
                    send(manager,cluster,invalidIds[i]);
                } catch ( Exception x ) {
                    log.error(sm.getString("ReplicationValve.send.invalid.failure",invalidIds[i]),x);
                }
            }
        }
    
protected voidsendMessage(org.apache.catalina.Session session, org.apache.catalina.ha.ClusterManager manager, org.apache.catalina.ha.CatalinaCluster cluster)
Send message delta message from request session

param
request current request
param
manager session manager
param
cluster replication cluster

        String id = session.getIdInternal();
        if (id != null) {
            send(manager, cluster, id);
        }
    
protected voidsendReplicationMessage(org.apache.catalina.connector.Request request, long totalstart, boolean isCrossContext, org.apache.catalina.ha.ClusterManager clusterManager, org.apache.catalina.ha.CatalinaCluster containerCluster)

param
request
param
totalstart
param
isCrossContext
param
clusterManager
param
containerCluster

        //this happens after the request
        long start = 0;
        if(doStatistics()) {
            start = System.currentTimeMillis();
        }
        try {
            // send invalid sessions
            // DeltaManager returns String[0]
            if (!(clusterManager instanceof DeltaManager))
                sendInvalidSessions(clusterManager, containerCluster);
            // send replication
            sendSessionReplicationMessage(request, clusterManager, containerCluster);
            if(isCrossContext)
                sendCrossContextSession(containerCluster);
        } catch (Exception x) {
            // FIXME we have a lot of sends, but the trouble with one node stops the correct replication to other nodes!
            log.error(sm.getString("ReplicationValve.send.failure"), x);
        } finally {
            // FIXME this stats update are not cheap!!
            if(doStatistics()) {
                updateStats(totalstart,start);
            }
        }
    
protected voidsendSessionReplicationMessage(org.apache.catalina.connector.Request request, org.apache.catalina.ha.ClusterManager manager, org.apache.catalina.ha.CatalinaCluster cluster)
Send Cluster Replication Request

param
request current request
param
manager session manager
param
cluster replication cluster

        Session session = request.getSessionInternal(false);
        if (session != null) {
            String uri = request.getDecodedRequestURI();
            // request without session change
            if (!isRequestWithoutSessionChange(uri)) {
                if (log.isDebugEnabled())
                    log.debug(sm.getString("ReplicationValve.invoke.uri", uri));
                sendMessage(session,manager,cluster);
            } else
                if(doStatistics())
                    nrOfFilterRequests++;
        }

    
public voidsetCluster(org.apache.catalina.ha.CatalinaCluster cluster)

param
cluster The cluster to set.

        this.cluster = cluster;
    
public voidsetFilter(java.lang.String filter)
compile filter string to regular expressions

see
Pattern#compile(java.lang.String)
param
filter The filter to set.

        if (log.isDebugEnabled())
            log.debug(sm.getString("ReplicationValve.filter.loading", filter));
        this.filter = filter;
        StringTokenizer t = new StringTokenizer(filter, ";");
        this.reqFilters = new Pattern[t.countTokens()];
        int i = 0;
        while (t.hasMoreTokens()) {
            String s = t.nextToken();
            if (log.isTraceEnabled())
                log.trace(sm.getString("ReplicationValve.filter.token", s));
            try {
                reqFilters[i++] = Pattern.compile(s);
            } catch (Exception x) {
                log.error(sm.getString("ReplicationValve.filter.token.failure",
                        s), x);
            }
        }
    
public voidsetPrimaryIndicator(boolean primaryIndicator)

param
primaryIndicator The primaryIndicator to set.

        this.primaryIndicator = primaryIndicator;
    
public voidsetPrimaryIndicatorName(java.lang.String primaryIndicatorName)

param
primaryIndicatorName The primaryIndicatorName to set.

        this.primaryIndicatorName = primaryIndicatorName;
    
protected voidsetReqFilters(java.util.regex.Pattern[] reqFilters)

param
reqFilters The reqFilters to set.

        this.reqFilters = reqFilters;
    
public voidsetStatistics(boolean doProcessingStats)
Set Calc processing stats

see
#resetStatistics()

        this.doProcessingStats = doProcessingStats;
    
public java.lang.StringtoString()
Return a String rendering of this object.


        StringBuffer sb = new StringBuffer("ReplicationValve[");
        if (container != null)
            sb.append(container.getName());
        sb.append("]");
        return (sb.toString());

    
protected voidupdateStats(long requestTime, long clusterTime)
protocol cluster replications stats

param
requestTime
param
clusterTime

        synchronized(this) {
            lastSendTime=System.currentTimeMillis();
            totalSendTime+=lastSendTime - clusterTime;
            totalRequestTime+=lastSendTime - requestTime;
            nrOfRequests++;
        }
        if(log.isInfoEnabled()) {
            if ( (nrOfRequests % 100) == 0 ) {
                 log.info(sm.getString("ReplicationValve.stats",
                     new Object[]{
                         new Long(totalRequestTime/nrOfRequests),
                         new Long(totalSendTime/nrOfRequests),
                         new Long(nrOfRequests),
                         new Long(nrOfSendRequests),
                         new Long(nrOfCrossContextSendRequests),
                         new Long(nrOfFilterRequests),
                         new Long(totalRequestTime),
                         new Long(totalSendTime)}));
             }
        }