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