001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: BigFatLock.java 79 2011-04-20 01:50:09Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.servlet;
009    
010    import java.io.IOException;
011    import java.util.concurrent.Callable;
012    
013    import javax.servlet.FilterChain;
014    import javax.servlet.ServletException;
015    import javax.servlet.http.HttpServletRequest;
016    import javax.servlet.http.HttpServletResponse;
017    
018    import org.slf4j.Logger;
019    import org.slf4j.LoggerFactory;
020    import org.springframework.web.filter.OncePerRequestFilter;
021    
022    /**
023     * Class for serializing processing using a big fat lock.
024     *
025     * <p>
026     * The lock can be acquired using {@link #runWithLock}. This class can also be used as a servlet filter.
027     *
028     * <p>
029     * Trace-level logging for displaying lock acquire and release events is included.
030     */
031    public final class BigFatLock extends OncePerRequestFilter {
032    
033        private static final Object BIG_FAT_LOCK = new BigFatLock();
034        private static final Logger LOG = LoggerFactory.getLogger(BigFatLock.class);
035    
036        @Override
037        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
038          FilterChain filterChain) throws ServletException, IOException {
039            if (LOG.isTraceEnabled()) {
040                LOG.trace("thread " + Thread.currentThread() + " waiting for " + BIG_FAT_LOCK);
041                synchronized (BIG_FAT_LOCK) {
042                    LOG.trace("thread " + Thread.currentThread() + " acquired " + BIG_FAT_LOCK);
043                    try {
044                        filterChain.doFilter(request, response);
045                    } finally {
046                        LOG.trace("thread " + Thread.currentThread() + " releasing " + BIG_FAT_LOCK);
047                    }
048                }
049            } else {
050                synchronized (BIG_FAT_LOCK) {
051                    filterChain.doFilter(request, response);
052                }
053            }
054        }
055    
056        /**
057         * Invoke the given action after acquiring the big fat lock.
058         */
059        public static void runWithLock(Runnable action) {
060            if (LOG.isTraceEnabled()) {
061                LOG.trace("thread " + Thread.currentThread() + " waiting for " + BIG_FAT_LOCK);
062                synchronized (BIG_FAT_LOCK) {
063                    LOG.trace("thread " + Thread.currentThread() + " acquired " + BIG_FAT_LOCK);
064                    try {
065                        action.run();
066                    } finally {
067                        LOG.trace("thread " + Thread.currentThread() + " releasing " + BIG_FAT_LOCK);
068                    }
069                }
070            } else {
071                synchronized (BIG_FAT_LOCK) {
072                    action.run();
073                }
074            }
075        }
076    
077        /**
078         * Invoke the given action after acquiring the big fat lock.
079         */
080        public static <V> V runWithLock(Callable<V> action) throws Exception {
081            if (LOG.isTraceEnabled()) {
082                LOG.trace("thread " + Thread.currentThread() + " waiting for " + BIG_FAT_LOCK);
083                synchronized (BIG_FAT_LOCK) {
084                    LOG.trace("thread " + Thread.currentThread() + " acquired " + BIG_FAT_LOCK);
085                    try {
086                        return action.call();
087                    } finally {
088                        LOG.trace("thread " + Thread.currentThread() + " releasing " + BIG_FAT_LOCK);
089                    }
090                }
091            } else {
092                synchronized (BIG_FAT_LOCK) {
093                    return action.call();
094                }
095            }
096        }
097    
098        /**
099         * Verify that the current thread is holding the big fat lock.
100         */
101        public static boolean isLockHeld() {
102            return Thread.holdsLock(BIG_FAT_LOCK);
103        }
104    
105        @Override
106        public String toString() {
107            return "BigFatLock@" + System.identityHashCode(this);
108        }
109    }
110