Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * 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.valves;
 
 import java.util.Date;
 import java.util.List;
 
 
This valve allows to detect requests that take a long time to process, which might indicate that the thread that is processing it is stuck. Based on code proposed by TomLu in Bugzilla entry #50306

Author(s):
slaurent
 
 public class StuckThreadDetectionValve extends ValveBase {

    
Keeps count of the number of stuck threads detected
 
     private final AtomicInteger stuckCount = new AtomicInteger(0);

    
In seconds. Default 600 (10 minutes).
 
     private int threshold = 600;

    
The only references we keep to actual running Thread objects are in this Map (which is automatically cleaned in invoke()s finally clause). That way, Threads can be GC'ed, eventhough the Valve still thinks they are stuck (caused by a long monitor interval)
 
     private final ConcurrentHashMap<LongMonitoredThreadactiveThreads =
             new ConcurrentHashMap<LongMonitoredThread>();
    
 
             new ConcurrentLinkedQueue<CompletedStuckThread>();

    
Specify the threshold (in seconds) used when checking for stuck threads. If <=0, the detection is disabled. The default is 600 seconds.

Parameters:
threshold The new threshold in seconds
 
     public void setThreshold(int threshold) {
         this. = threshold;
     }

    

Returns:
The current threshold in seconds
See also:
setThreshold(int)
 
     public int getThreshold() {
         return ;
     }
 
 
     private void notifyStuckThreadDetected(MonitoredThread monitoredThread,
         long activeTimeint numStuckThreads) {
         Throwable th = new Throwable();
         th.setStackTrace(monitoredThread.getThread().getStackTrace());
             (monitoredThread.getThread().getName(), monitoredThread.getThread().getId(), 
                     activeTimemonitoredThread.getStartTime(), monitoredThread.getRequestUri(), numStuckThreadsth);
     }
 
     private void notifyStuckThreadCompleted(CompletedStuckThread thread,
             int numStuckThreads) {
            (thread.getName(), thread.getId(), thread.getTotalActiveTime(), numStuckThreads);
    }

    
    @Override
    public void invoke(Request requestResponse response)
            throws IOExceptionServletException {
        if ( <= 0) {
            // short-circuit if not monitoring stuck threads
            getNext().invoke(requestresponse);
            return;
        }
        // Save the thread/runnable
        // Keeping a reference to the thread object here does not prevent
        // GC'ing, as the reference is removed from the Map in the finally clause
        Long key = Long.valueOf(Thread.currentThread().getId());
        StringBuffer requestUrl = request.getRequestURL();
        if(request.getQueryString()!=null) {
            requestUrl.append("?");
            requestUrl.append(request.getQueryString());
        }
        MonitoredThread monitoredThread = new MonitoredThread(Thread.currentThread(),
            requestUrl.toString());
        .put(keymonitoredThread);
        try {
            getNext().invoke(requestresponse);
        } finally {
            .remove(key);
            if (monitoredThread.markAsDone() == .) {
                .add(
                        new CompletedStuckThread(monitoredThread.getThread(),
                            monitoredThread.getActiveTimeInMillis()));
            }
        }
    }
    @Override
    public void event(Request requestResponse responseHttpEvent event)
            throws IOExceptionServletException {
        if ( <= 0) {
            // short-circuit if not monitoring stuck threads
            getNext().event(requestresponseevent);
            return;
        }
        // Save the thread/runnable
        // Keeping a reference to the thread object here does not prevent
        // GC'ing, as the reference is removed from the Map in the finally clause
        Long key = Long.valueOf(Thread.currentThread().getId());
        StringBuffer requestUrl = request.getRequestURL();
        if(request.getQueryString()!=null) {
            requestUrl.append("?");
            requestUrl.append(request.getQueryString());
        }
        MonitoredThread monitoredThread = new MonitoredThread(Thread.currentThread(),
            requestUrl.toString());
        .put(keymonitoredThread);
        try {
            getNext().event(requestresponseevent);
        } finally {
            .remove(key);
            if (monitoredThread.markAsDone() == .) {
                .add(
                        new CompletedStuckThread(monitoredThread.getThread(),
                            monitoredThread.getActiveTimeInMillis()));
            }
        }
    }
    @Override
    public void backgroundProcess() {
        super.backgroundProcess();
        long thresholdInMillis =  * 1000;
        // Check monitored threads, being careful that the request might have
        // completed by the time we examine it
        for (MonitoredThread monitoredThread : .values()) {
            long activeTime = monitoredThread.getActiveTimeInMillis();
            if (activeTime >= thresholdInMillis && monitoredThread.markAsStuckIfStillRunning()) {
                int numStuckThreads = .incrementAndGet();
                notifyStuckThreadDetected(monitoredThreadactiveTimenumStuckThreads);
            }
        }
        // Check if any threads previously reported as stuck, have finished.
        for (CompletedStuckThread completedStuckThread = .poll();
            completedStuckThread != nullcompletedStuckThread = .poll()) {
            int numStuckThreads = .decrementAndGet();
            notifyStuckThreadCompleted(completedStuckThreadnumStuckThreads);
        }
    }
    public long[] getStuckThreadIds() {
        List<LongidList = new ArrayList<Long>();
        for (MonitoredThread monitoredThread : .values()) {
            if (monitoredThread.isMarkedAsStuck()) {
                idList.add(Long.valueOf(monitoredThread.getThread().getId()));
            }
        }
        long[] result = new long[idList.size()];
        for (int i = 0; i < result.lengthi++) {
            result[i] = idList.get(i).longValue();
        }
        return result;
    }
    private static class MonitoredThread {

        
Reference to the thread to get a stack trace from background task
        private final Thread thread;
        private final String requestUri;
        private final long start;
        private final AtomicInteger state = new AtomicInteger(
            ..ordinal());
        public MonitoredThread(Thread threadString requestUri) {
            this. = thread;
            this. = requestUri;
            this. = System.currentTimeMillis();
        }
        public Thread getThread() {
            return this.;
        }
        public String getRequestUri() {
            return ;
        }
        public long getActiveTimeInMillis() {
            return System.currentTimeMillis() - ;
        }
        public Date getStartTime() {
            return new Date();
        }
        public boolean markAsStuckIfStillRunning() {
            return this..compareAndSet(..ordinal(),
                ..ordinal());
        }
        public MonitoredThreadState markAsDone() {
            int val = this..getAndSet(..ordinal());
            return MonitoredThreadState.values()[val];
        }
        boolean isMarkedAsStuck() {
            return this..get() == ..ordinal();
        }
    }
    private static class CompletedStuckThread {
        private final String threadName;
        private final long threadId;
        private final long totalActiveTime;
        public CompletedStuckThread(Thread threadlong totalActiveTime) {
            this. = thread.getName();
            this. = thread.getId();
            this. = totalActiveTime;
        }
        public String getName() {
            return this.;
        }
        public long getId() {
            return this.;
        }
        public long getTotalActiveTime() {
            return this.;
        }
    }
    private enum MonitoredThreadState {
        RUNNING, STUCK, DONE;
    }
New to GrepCode? Check out our FAQ X