IdempotentSequence.java
Upload User: demmber
Upload Date: 2007-12-22
Package Size: 717k
Code Size: 12k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * @(#)IdempotentSequence.java 0.3-3 06/05/2001
  3.  *
  4.  *  This file is part of the HTTPClient package
  5.  *  Copyright (C) 1996-2001 Ronald Tschal鋜
  6.  *
  7.  *  This library is free software; you can redistribute it and/or
  8.  *  modify it under the terms of the GNU Lesser General Public
  9.  *  License as published by the Free Software Foundation; either
  10.  *  version 2 of the License, or (at your option) any later version.
  11.  *
  12.  *  This library is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  *  Lesser General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU Lesser General Public
  18.  *  License along with this library; if not, write to the Free
  19.  *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20.  *  MA 02111-1307, USA
  21.  *
  22.  *  For questions, suggestions, bug-reports, enhancement-requests etc.
  23.  *  I may be contacted at:
  24.  *
  25.  *  ronald@innovation.ch
  26.  *
  27.  *  The HTTPClient's home page is located at:
  28.  *
  29.  *  http://www.innovation.ch/java/HTTPClient/ 
  30.  *
  31.  */
  32. package HTTPClient;
  33. import java.util.Hashtable;
  34. import java.util.Enumeration;
  35. /**
  36.  * <P>This class checks whether a sequence of requests is idempotent. This
  37.  * is used to determine which requests may be automatically retried. This
  38.  * class also serves as a central place to record which methods have side
  39.  * effects and which methods are idempotent.
  40.  *
  41.  * <P>Note: unknown methods (i.e. a method which is not HEAD, GET, POST, PUT,
  42.  * DELETE, OPTIONS, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, or
  43.  * UNLOCK) are treated conservatively, meaning they are assumed to have side
  44.  * effects and are not idempotent.
  45.  *
  46.  * <P>Usage:
  47.  * <PRE>
  48.  *     IdempotentSequence seq = new IdempotentSequence();
  49.  *     seq.add(r1);
  50.  *     ...
  51.  *     if (seq.isIdempotent(r1)) ...
  52.  *     ...
  53.  * </PRE>
  54.  *
  55.  * @version 0.3-3  06/05/2001
  56.  * @author Ronald Tschal鋜
  57.  */
  58. class IdempotentSequence
  59. {
  60.     /** method number definitions */
  61.     private static final int UNKNOWN = 0,
  62.      HEAD    = 1,
  63.      GET     = 2,
  64.      POST    = 3,
  65.      PUT     = 4,
  66.      DELETE  = 5,
  67.      OPTIONS = 6,
  68.      TRACE   = 7,
  69.      // DAV methods
  70.      PROPFIND  = 8,
  71.      PROPPATCH = 9,
  72.      MKCOL     = 10,
  73.      COPY      = 11,
  74.      MOVE      = 12,
  75.      LOCK      = 13,
  76.      UNLOCK    = 14;
  77.     /** these are the history of previous requests */
  78.     private int[]    m_history;
  79.     private String[] r_history;
  80.     private int      m_len, r_len;
  81.     /** trigger analysis of threads */
  82.     private boolean   analysis_done = false;
  83.     private Hashtable threads = new Hashtable();
  84.     // Constructors
  85.     /**
  86.      * Start a new sequence of requests.
  87.      */
  88.     public IdempotentSequence()
  89.     {
  90. m_history = new int[10];
  91. r_history = new String[10];
  92. m_len = 0;
  93. r_len = 0;
  94.     }
  95.     // Methods
  96.     /**
  97.      * Add the request to the end of the list of requests. This is used
  98.      * to build the complete sequence of requests before determining
  99.      * whether the sequence is idempotent.
  100.      *
  101.      * @param req the next request
  102.      */
  103.     public void add(Request req)
  104.     {
  105. if (m_len >= m_history.length)
  106.     m_history = Util.resizeArray(m_history, m_history.length+10);
  107. m_history[m_len++] = methodNum(req.getMethod());
  108. if (r_len >= r_history.length)
  109.     r_history = Util.resizeArray(r_history, r_history.length+10);
  110. r_history[r_len++] = req.getRequestURI();
  111.     }
  112.     /**
  113.      * Is this request part of an idempotent sequence? This method <em>must
  114.      * not</em> be called before all requests have been added to this
  115.      * sequence; similarly, <var>add()</var> <em>must not</em> be called
  116.      * after this method was invoked.
  117.      *
  118.      * <P>We split up the sequence of requests into individual sub-sequences,
  119.      * or threads, with all requests in a thread having the same request-URI
  120.      * and no two threads having the same request-URI. Each thread is then
  121.      * marked as idempotent or not according to the following rules:
  122.      *
  123.      * <OL>
  124.      * <LI>If any method is UNKNOWN then the thread is not idempotent;
  125.      * <LI>else, if no method has side effects then the thread is idempotent;
  126.      * <LI>else, if the first method has side effects and is complete then
  127.      *     the thread is idempotent;
  128.      * <LI>else, if the first method has side effects, is not complete,
  129.      *     and no other method has side effects then the thread is idempotent;
  130.      * <LI>else the thread is not idempotent.
  131.      * </OL>
  132.      *
  133.      * <P>The major assumption here is that the side effects of any method
  134.      * only apply to resource specified. E.g. a <tt>"PUT /barbara.html"</tt>
  135.      * will only affect the resource "/barbara.html" and nothing else.
  136.      * This assumption is violated by POST of course; however, POSTs are
  137.      * not pipelined and will therefore never show up here.
  138.      *
  139.      * @param req the request
  140.      */
  141.     public boolean isIdempotent(Request req)
  142.     {
  143. if (!analysis_done)
  144.     do_analysis();
  145. return ((Boolean) threads.get(req.getRequestURI())).booleanValue();
  146.     }
  147.     private static final Object INDET = new Object();
  148.     private void do_analysis()
  149.     {
  150. for (int idx=0; idx<r_len; idx++)
  151. {
  152.     Object t_state = threads.get(r_history[idx]);
  153.     if (m_history[idx] == UNKNOWN)
  154. threads.put(r_history[idx], Boolean.FALSE);
  155.     else if (t_state == null) // new thread
  156.     {
  157. if (methodHasSideEffects(m_history[idx]))
  158. {
  159.     if (methodIsComplete(m_history[idx])) // is idempotent
  160. threads.put(r_history[idx], Boolean.TRUE);
  161.     else
  162. threads.put(r_history[idx], Boolean.FALSE);
  163. }
  164. else // indeterminate
  165.     threads.put(r_history[idx], INDET);
  166.     }
  167.     else // update thread
  168.     {
  169. if (t_state == INDET  && methodHasSideEffects(m_history[idx]))
  170.     threads.put(r_history[idx], Boolean.FALSE);
  171.     }
  172. }
  173. // any thread still indeterminate must be idempotent
  174. Enumeration te = threads.keys();
  175. while (te.hasMoreElements())
  176. {
  177.     String res = (String) te.nextElement();
  178.     if (threads.get(res) == INDET)
  179. threads.put(res, Boolean.TRUE);
  180. }
  181.     }
  182.     /**
  183.      * A method is idempotent if the side effects of N identical
  184.      * requests is the same as for a single request (Section 9.1.2
  185.      * of RFC-????).
  186.      *
  187.      * @return true if method is idempotent
  188.      */
  189.     public static boolean methodIsIdempotent(String method)
  190.     {
  191. return methodIsIdempotent(methodNum(method));
  192.     }
  193.     private static boolean methodIsIdempotent(int method)
  194.     {
  195. switch (method)
  196. {
  197.     case HEAD:
  198.     case GET:
  199.     case PUT:
  200.     case DELETE:
  201.     case OPTIONS:
  202.     case TRACE:
  203.     case PROPFIND:
  204.     case PROPPATCH:
  205.     case COPY:
  206.     case MOVE:
  207. return true;
  208.     case UNKNOWN:
  209.     case POST:
  210.     case MKCOL:
  211.     case LOCK:
  212.     case UNLOCK:
  213.     default:
  214. return false;
  215. }
  216.     }
  217.     /**
  218.      * A method is complete if any side effects of the request affect
  219.      * the complete resource. For example, a PUT is complete but a
  220.      * PUT with byte-ranges wouldn't be. In essence, if a request uses
  221.      * a method which has side effects and is complete then the state
  222.      * of the resource after the request is independent of the state of
  223.      * the resource before the request.
  224.      *
  225.      * @return true if method is complete
  226.      */
  227.     public static boolean methodIsComplete(String method)
  228.     {
  229. return methodIsComplete(methodNum(method));
  230.     }
  231.     private static boolean methodIsComplete(int method)
  232.     {
  233. switch (method)
  234. {
  235.     case HEAD:
  236.     case GET:
  237.     case PUT:
  238.     case DELETE:
  239.     case OPTIONS:
  240.     case TRACE:
  241.     case PROPFIND:
  242.     case COPY:
  243.     case MOVE:
  244.     case LOCK:
  245.     case UNLOCK:
  246. return true;
  247.     case UNKNOWN:
  248.     case POST:
  249.     case PROPPATCH:
  250.     case MKCOL:
  251.     default:
  252. return false;
  253. }
  254.     }
  255.     public static boolean methodHasSideEffects(String method)
  256.     {
  257. return methodHasSideEffects(methodNum(method));
  258.     }
  259.     private static boolean methodHasSideEffects(int method)
  260.     {
  261. switch (method)
  262. {
  263.     case HEAD:
  264.     case GET:
  265.     case OPTIONS:
  266.     case TRACE:
  267.     case PROPFIND:
  268.     case LOCK:
  269.     case UNLOCK:
  270. return false;
  271.     case UNKNOWN:
  272.     case POST:
  273.     case PUT:
  274.     case DELETE:
  275.     case PROPPATCH:
  276.     case MKCOL:
  277.     case COPY:
  278.     case MOVE:
  279.     default:
  280. return true;
  281. }
  282.     }
  283.     private static int methodNum(String method)
  284.     {
  285. if (method.equals("GET"))
  286.     return GET;
  287. if (method.equals("POST"))
  288.     return POST;
  289. if (method.equals("HEAD"))
  290.     return HEAD;
  291. if (method.equals("PUT"))
  292.     return PUT;
  293. if (method.equals("DELETE"))
  294.     return DELETE;
  295. if (method.equals("OPTIONS"))
  296.     return OPTIONS;
  297. if (method.equals("TRACE"))
  298.     return TRACE;
  299. if (method.equals("PROPFIND"))
  300.     return PROPFIND;
  301. if (method.equals("PROPPATCH"))
  302.     return PROPPATCH;
  303. if (method.equals("MKCOL"))
  304.     return MKCOL;
  305. if (method.equals("COPY"))
  306.     return COPY;
  307. if (method.equals("MOVE"))
  308.     return MOVE;
  309. if (method.equals("LOCK"))
  310.     return LOCK;
  311. if (method.equals("UNLOCK"))
  312.     return UNLOCK;
  313. return UNKNOWN;
  314.     }
  315.     /**
  316.      * Test code.
  317.      */
  318.     public static void main(String args[])
  319.     {
  320. IdempotentSequence seq = new IdempotentSequence();
  321. seq.add(new Request(null, "GET", "/b1", null, null, null, false));
  322. seq.add(new Request(null, "PUT", "/b2", null, null, null, false));
  323. seq.add(new Request(null, "GET", "/b1", null, null, null, false));
  324. seq.add(new Request(null, "PUT", "/b3", null, null, null, false));
  325. seq.add(new Request(null, "GET", "/b2", null, null, null, false));
  326. seq.add(new Request(null, "POST", "/b8", null, null, null, false));
  327. seq.add(new Request(null, "PUT", "/b3", null, null, null, false));
  328. seq.add(new Request(null, "GET", "/b1", null, null, null, false));
  329. seq.add(new Request(null, "TRACE", "/b4", null, null, null, false));
  330. seq.add(new Request(null, "GET", "/b9", null, null, null, false));
  331. seq.add(new Request(null, "LINK", "/b4", null, null, null, false));
  332. seq.add(new Request(null, "GET", "/b4", null, null, null, false));
  333. seq.add(new Request(null, "PUT", "/b5", null, null, null, false));
  334. seq.add(new Request(null, "HEAD", "/b5", null, null, null, false));
  335. seq.add(new Request(null, "PUT", "/b5", null, null, null, false));
  336. seq.add(new Request(null, "POST", "/b9", null, null, null, false));
  337. seq.add(new Request(null, "GET", "/b6", null, null, null, false));
  338. seq.add(new Request(null, "DELETE", "/b6", null, null, null, false));
  339. seq.add(new Request(null, "HEAD", "/b6", null, null, null, false));
  340. seq.add(new Request(null, "OPTIONS", "/b7", null, null, null, false));
  341. seq.add(new Request(null, "TRACE", "/b7", null, null, null, false));
  342. seq.add(new Request(null, "GET", "/b7", null, null, null, false));
  343. seq.add(new Request(null, "PUT", "/b7", null, null, null, false));
  344. if (!seq.isIdempotent(new Request(null, null, "/b1", null, null, null, false)))
  345.     System.err.println("Sequence b1 failed");
  346. if (!seq.isIdempotent(new Request(null, null, "/b2", null, null, null, false)))
  347.     System.err.println("Sequence b2 failed");
  348. if (!seq.isIdempotent(new Request(null, null, "/b3", null, null, null, false)))
  349.     System.err.println("Sequence b3 failed");
  350. if (seq.isIdempotent(new Request(null, null, "/b4", null, null, null, false)))
  351.     System.err.println("Sequence b4 failed");
  352. if (!seq.isIdempotent(new Request(null, null, "/b5", null, null, null, false)))
  353.     System.err.println("Sequence b5 failed");
  354. if (seq.isIdempotent(new Request(null, null, "/b6", null, null, null, false)))
  355.     System.err.println("Sequence b6 failed");
  356. if (seq.isIdempotent(new Request(null, null, "/b7", null, null, null, false)))
  357.     System.err.println("Sequence b7 failed");
  358. if (seq.isIdempotent(new Request(null, null, "/b8", null, null, null, false)))
  359.     System.err.println("Sequence b8 failed");
  360. if (seq.isIdempotent(new Request(null, null, "/b9", null, null, null, false)))
  361.     System.err.println("Sequence b9 failed");
  362. System.out.println("Tests finished");
  363.     }
  364. }