Package com.knife.web.threading

Source Code of com.knife.web.threading.DoubleSubmitLock

/*
*  Copyright 2012 Anton Van Zyl. http://code.google.com/p/java-swiss-knife/
*
*  Licensed 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.
*  under the License.
*/
package com.knife.web.threading;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Hex;

import com.knife.web.threading.exception.InvalidRequestTokenException;

/**
* This is a thread lock that only allows one thread to enter and ignoring extra
* threads until the first thread is completed and then letting the user
* continue to the result page.
*
* <br/>
* <br/>
* Please visit <a
* href="http://code.google.com/p/java-swiss-knife/">Java-Swiss-Knife</a> and
* comment, rate, contribute or raise a issue/enhancement for my library. <br/>
*
* @author Anton Van Zyl
*
*/
public class DoubleSubmitLock {

  private static final String SESSION_REQ_ID = "session_req_id";

  private static SecureRandom secureRandom;

  private final String uniqueRequestId;
  private final Lock controlLock = new ReentrantLock();
  private final Lock mainLock = new ReentrantLock();
  private final Lock subLock = new ReentrantLock();
  private final Condition cond = subLock.newCondition();
  private boolean finished;

  private String jspReturnPage;
  private Map<String, Object> jspReturnPageData = new HashMap<String, Object>();

  static {
    try {
      secureRandom = SecureRandom.getInstance("SHA1PRNG");
    } catch (NoSuchAlgorithmException e) {
      System.out.println("getUniqueRequestIdenitfier: " + e.getMessage());
      e.printStackTrace();
    }
  }

  public DoubleSubmitLock(String uniqueRequestId) {
    this.uniqueRequestId = uniqueRequestId;
  }

  public boolean isSameRequestId(String requestId) {
    return uniqueRequestId.equalsIgnoreCase(requestId);
  }

  public boolean tryGetLock(String errorJspPage, Map<String, Object> jspErrorPageData) throws InterruptedException {

    // obtain controlLock so we can also get subLock before the main thread
    // can signal release - and cause us to wait forever
    controlLock.lock();

    if (mainLock.tryLock()) {
      System.out.println("Lock obtained [id=" + uniqueRequestId + "]");
      jspReturnPage = errorJspPage;
      jspReturnPageData = jspErrorPageData;
      controlLock.unlock();
      return true;
    } else {
      try {
        System.out.println("Lock NOT obtained; blocking [id=" + uniqueRequestId + "]");
        subLock.lock();
        // we have the subLock; can release controlLock now before we
        // enter the wait
        controlLock.unlock();
        cond.await();
        System.out.println("Blocking released [id=" + uniqueRequestId + "]");
      } finally {
        subLock.unlock();
      }
    }
    return false;
  }

  public void unlock() {
    unlock(true);
  }

  public void unlock(boolean completed) {

    controlLock.lock();
    System.out.println("Lock released [id=" + uniqueRequestId + "; completed=" + completed + "]");
    try {
      finished = completed;
      subLock.lock();
      try {
        cond.signalAll();
      } finally {
        subLock.unlock();
      }
    } finally {
      controlLock.unlock();
    }
  }

  /**
   * @return the current JSP page
   */
  public String getJspReturnPage() {
    return this.jspReturnPage;
  }

  /**
   * @return The data the JSP page need to render correctly
   */
  public Map<String, Object> getJspReturnPageData() {
    return this.jspReturnPageData;
  }

  /**
   *
   * @param jspReturnPage
   */
  public void setJspReturnPage(String jspReturnPage) {
    this.jspReturnPage = jspReturnPage;
  }

  /**
   *
   * @param jspReturnPageData
   */
  public void setJspReturnPageData(Map<String, Object> jspReturnPageData) {
    this.jspReturnPageData = jspReturnPageData;
  }

  /**
   * @return Test if request is processed
   */
  public boolean isRequestAlreadyProcessed() {
    controlLock.lock();
    try {
      return finished;
    } finally {
      controlLock.unlock();
    }
  }

  /**
   * Generates a random Token and adds it to the session to be used in
   * validating incoming requests.<br/>
   * Use the validateUniqueRequestIdentifier method to validate the key value
   * against the session.<br/>
   */
  public static String createDoubleSubmitLock(HttpServletRequest request, String callerId) {
    byte[] bytes = new byte[32];
    secureRandom.nextBytes(bytes);
    String uniqueRequestId = Hex.encodeHexString(bytes);

    System.out.println("Created lock [id=" + uniqueRequestId + "]");

    request.getSession().setAttribute(getSessionId(callerId), new DoubleSubmitLock(uniqueRequestId));
    return uniqueRequestId;
  }

  private static String getSessionId(String callerId) {
    return SESSION_REQ_ID + ":" + callerId;
  }

  /**
   * This will validate the key in the session and if invalid throw an
   * exception to be handled and send the user to a generic double submit
   * page.<br/>
   *
   * @throws InvalidRequestTokenException
   */
  public static DoubleSubmitLock getDoubleSubmitLock(HttpServletRequest request, String callerId, String requestId) throws InvalidRequestTokenException {

    DoubleSubmitLock dsl = (DoubleSubmitLock) request.getSession().getAttribute(getSessionId(callerId));

    if ((dsl == null) || (!dsl.isSameRequestId(requestId))) {
      String msg = "DoubleSubmitLock not bound to session [callerId=" + callerId + "; requestId=" + requestId + "]";
      System.out.println(msg);
      throw new InvalidRequestTokenException(msg);
    }
    return dsl;
  }
}
TOP

Related Classes of com.knife.web.threading.DoubleSubmitLock

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.