/* * @(#)IdempotentSequence.java 0.3-3 06/05/2001 * * This file is part of the HTTPClient package * Copyright (C) 1996-2001 Ronald Tschalär * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA * * For questions, suggestions, bug-reports, enhancement-requests etc. * I may be contacted at: * * ronald@innovation.ch * * The HTTPClient's home page is located at: * * http://www.innovation.ch/java/HTTPClient/ * */ package HTTPClient; import java.util.Hashtable; import java.util.Enumeration; /** *

This class checks whether a sequence of requests is idempotent. This * is used to determine which requests may be automatically retried. This * class also serves as a central place to record which methods have side * effects and which methods are idempotent. * *

Note: unknown methods (i.e. a method which is not HEAD, GET, POST, PUT, * DELETE, OPTIONS, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, or * UNLOCK) are treated conservatively, meaning they are assumed to have side * effects and are not idempotent. * *

Usage: *

 *     IdempotentSequence seq = new IdempotentSequence();
 *     seq.add(r1);
 *     ...
 *     if (seq.isIdempotent(r1)) ...
 *     ...
 * 
* * @version 0.3-3 06/05/2001 * @author Ronald Tschalär */ class IdempotentSequence { /** method number definitions */ private static final int UNKNOWN = 0, HEAD = 1, GET = 2, POST = 3, PUT = 4, DELETE = 5, OPTIONS = 6, TRACE = 7, // DAV methods PROPFIND = 8, PROPPATCH = 9, MKCOL = 10, COPY = 11, MOVE = 12, LOCK = 13, UNLOCK = 14; /** these are the history of previous requests */ private int[] m_history; private String[] r_history; private int m_len, r_len; /** trigger analysis of threads */ private boolean analysis_done = false; private Hashtable threads = new Hashtable(); // Constructors /** * Start a new sequence of requests. */ public IdempotentSequence() { m_history = new int[10]; r_history = new String[10]; m_len = 0; r_len = 0; } // Methods /** * Add the request to the end of the list of requests. This is used * to build the complete sequence of requests before determining * whether the sequence is idempotent. * * @param req the next request */ public void add(Request req) { if (m_len >= m_history.length) m_history = Util.resizeArray(m_history, m_history.length+10); m_history[m_len++] = methodNum(req.getMethod()); if (r_len >= r_history.length) r_history = Util.resizeArray(r_history, r_history.length+10); r_history[r_len++] = req.getRequestURI(); } /** * Is this request part of an idempotent sequence? This method must * not be called before all requests have been added to this * sequence; similarly, add() must not be called * after this method was invoked. * *

We split up the sequence of requests into individual sub-sequences, * or threads, with all requests in a thread having the same request-URI * and no two threads having the same request-URI. Each thread is then * marked as idempotent or not according to the following rules: * *

    *
  1. If any method is UNKNOWN then the thread is not idempotent; *
  2. else, if no method has side effects then the thread is idempotent; *
  3. else, if the first method has side effects and is complete then * the thread is idempotent; *
  4. else, if the first method has side effects, is not complete, * and no other method has side effects then the thread is idempotent; *
  5. else the thread is not idempotent. *
* *

The major assumption here is that the side effects of any method * only apply to resource specified. E.g. a "PUT /barbara.html" * will only affect the resource "/barbara.html" and nothing else. * This assumption is violated by POST of course; however, POSTs are * not pipelined and will therefore never show up here. * * @param req the request */ public boolean isIdempotent(Request req) { if (!analysis_done) do_analysis(); return ((Boolean) threads.get(req.getRequestURI())).booleanValue(); } private static final Object INDET = new Object(); private void do_analysis() { for (int idx=0; idx