KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > IdempotentSequence


1 /*
2  * @(#)IdempotentSequence.java 0.3-2 18/06/1999
3  *
4  * This file is part of the HTTPClient package
5  * Copyright (C) 1996-1999 Ronald Tschalär
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  */

28
29 package HTTPClient;
30
31
32 import java.util.Hashtable JavaDoc;
33 import java.util.Enumeration JavaDoc;
34
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,
42  * PUT, DELETE, OPTIONS or TRACE) are treated conservatively, meaning
43  * they are assumed to have side effects and are not idempotent.
44  *
45  * <P>Usage:
46  * <PRE>
47  * IdempotentSequence seq = new IdempotentSequence();
48  * seq.add(r1);
49  * ...
50  * if (seq.isIdempotent(r1)) ...
51  * ...
52  * </PRE>
53  *
54  * @version 0.3-2 18/06/1999
55  * @author Ronald Tschalär
56  */

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
70     /** these are the history of previous requests */
71     private int[] m_history;
72     private String JavaDoc[] r_history;
73     private int m_len, r_len;
74
75     /** trigger analysis of threads */
76     private boolean analysis_done = false;
77     private Hashtable JavaDoc threads = new Hashtable JavaDoc();
78
79
80     // Constructors
81

82     /**
83      * Start a new sequence of requests.
84      */

85     public IdempotentSequence()
86     {
87     m_history = new int[10];
88     r_history = new String JavaDoc[10];
89     m_len = 0;
90     r_len = 0;
91     }
92
93
94     // Methods
95

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
109     if (r_len >= r_history.length)
110         r_history = Util.resizeArray(r_history, r_history.length+10);
111     r_history[r_len++] = req.getRequestURI();
112     }
113
114
115     /**
116      * Is this request part of an idempotent sequence? This method <em>must
117      * not</em> be called before all requests have been added to this
118      * sequence; similarly, <var>add()</var> <em>must not</em> be called
119      * after this method was invoked.
120      *
121      * <P>We split up the sequence of requests into individual sub-sequences,
122      * or threads, with all requests in a thread having the same request-URI
123      * and no two threads having the same request-URI. Each thread is then
124      * marked as idempotent or not according to the following rules:
125      *
126      * <OL>
127      * <LI>If any method is UNKNOWN then the thread is not idempotent;
128      * <LI>else, if no method has side effects then the thread is idempotent;
129      * <LI>else, if the first method has side effects and is complete then
130      * the thread is idempotent;
131      * <LI>else, if the first method has side effects, is not complete,
132      * and no other method has side effects then the thread is idempotent;
133      * <LI>else the thread is not idempotent.
134      * </OL>
135      *
136      * <P>The major assumption here is that the side effects of any method
137      * only apply to resource specified. E.g. a <tt>"PUT /barbara.html"</tt>
138      * will only affect the resource "/barbara.html" and nothing else.
139      * This assumption is violated by POST of course; however, POSTs are
140      * not pipelined and will therefore never show up here.
141      *
142      * @param req the request
143      */

144     public boolean isIdempotent(Request req)
145     {
146     if (!analysis_done)
147         do_analysis();
148
149     return ((Boolean JavaDoc) threads.get(req.getRequestURI())).booleanValue();
150     }
151
152
153     private static final Object JavaDoc INDET = new Object JavaDoc();
154
155     private void do_analysis()
156     {
157     for (int idx=0; idx<r_len; idx++)
158     {
159         Object JavaDoc t_state = threads.get(r_history[idx]);
160
161         if (m_history[idx] == UNKNOWN)
162         threads.put(r_history[idx], Boolean.FALSE);
163         else if (t_state == null) // new thread
164
{
165         if (methodHasSideEffects(m_history[idx]) &&
166             methodIsComplete(m_history[idx])) // is idempotent
167
threads.put(r_history[idx], Boolean.TRUE);
168         else // indeterminate
169
threads.put(r_history[idx], INDET);
170         }
171         else // update thread
172
{
173         if (t_state == INDET && methodHasSideEffects(m_history[idx]))
174             threads.put(r_history[idx], Boolean.FALSE);
175         }
176     }
177
178     // any thread still indeterminate must be idempotent
179
Enumeration JavaDoc te = threads.keys();
180     while (te.hasMoreElements())
181     {
182         String JavaDoc res = (String JavaDoc) te.nextElement();
183         if (threads.get(res) == INDET)
184         threads.put(res, Boolean.TRUE);
185     }
186     }
187
188
189     /**
190      * A method is idempotent if the side effects of N identical
191      * requests is the same as for a single request (Section 9.1.2
192      * of RFC-????).
193      *
194      * @return true if method is idempotent
195      */

196     public static boolean methodIsIdempotent(String JavaDoc method)
197     {
198     return methodIsIdempotent(methodNum(method));
199     }
200
201
202     private static boolean methodIsIdempotent(int method)
203     {
204     switch (method)
205     {
206         case HEAD:
207         case GET:
208         case PUT:
209         case DELETE:
210         case OPTIONS:
211         case TRACE:
212         return true;
213         default:
214         return false;
215     }
216     }
217
218
219     /**
220      * A method is complete if any side effects of the request affect
221      * the complete resource. For example, a PUT is complete but a
222      * PUT with byte-ranges wouldn't be. In essence, if a request uses
223      * a method which has side effects and is complete then the state
224      * of the resource after the request is independent of the state of
225      * the resource before the request.
226      *
227      * @return true if method is complete
228      */

229     public static boolean methodIsComplete(String JavaDoc method)
230     {
231     return methodIsComplete(methodNum(method));
232     }
233
234
235     private static boolean methodIsComplete(int method)
236     {
237     switch (method)
238     {
239         case HEAD:
240         case GET:
241         case PUT:
242         case DELETE:
243         case OPTIONS:
244         case TRACE:
245         return true;
246         default:
247         return false;
248     }
249     }
250
251
252     public static boolean methodHasSideEffects(String JavaDoc method)
253     {
254     return methodHasSideEffects(methodNum(method));
255     }
256
257
258     private static boolean methodHasSideEffects(int method)
259     {
260     switch (method)
261     {
262         case HEAD:
263         case GET:
264         case OPTIONS:
265         case TRACE:
266         return false;
267         default:
268         return true;
269     }
270     }
271
272
273     private static int methodNum(String JavaDoc method)
274     {
275     if (method.equals("GET"))
276         return GET;
277     if (method.equals("POST"))
278         return POST;
279     if (method.equals("HEAD"))
280         return HEAD;
281     if (method.equals("PUT"))
282         return PUT;
283     if (method.equals("DELETE"))
284         return DELETE;
285     if (method.equals("OPTIONS"))
286         return OPTIONS;
287     if (method.equals("TRACE"))
288         return TRACE;
289
290     return UNKNOWN;
291     }
292
293
294     /**
295      * Test code.
296      */

297     public static void main(String JavaDoc args[])
298     {
299     IdempotentSequence seq = new IdempotentSequence();
300
301     seq.add(new Request(null, "GET", "/b1", null, null, null, false));
302     seq.add(new Request(null, "PUT", "/b2", null, null, null, false));
303     seq.add(new Request(null, "GET", "/b1", null, null, null, false));
304     seq.add(new Request(null, "PUT", "/b3", null, null, null, false));
305     seq.add(new Request(null, "GET", "/b2", null, null, null, false));
306     seq.add(new Request(null, "PUT", "/b3", null, null, null, false));
307     seq.add(new Request(null, "GET", "/b1", null, null, null, false));
308     seq.add(new Request(null, "TRACE", "/b4", null, null, null, false));
309     seq.add(new Request(null, "LINK", "/b4", null, null, null, false));
310     seq.add(new Request(null, "GET", "/b4", null, null, null, false));
311     seq.add(new Request(null, "PUT", "/b5", null, null, null, false));
312     seq.add(new Request(null, "HEAD", "/b5", null, null, null, false));
313     seq.add(new Request(null, "PUT", "/b5", null, null, null, false));
314     seq.add(new Request(null, "GET", "/b6", null, null, null, false));
315     seq.add(new Request(null, "DELETE", "/b6", null, null, null, false));
316     seq.add(new Request(null, "HEAD", "/b6", null, null, null, false));
317     seq.add(new Request(null, "OPTIONS", "/b7", null, null, null, false));
318     seq.add(new Request(null, "TRACE", "/b7", null, null, null, false));
319     seq.add(new Request(null, "GET", "/b7", null, null, null, false));
320     seq.add(new Request(null, "PUT", "/b7", null, null, null, false));
321
322     if (!seq.isIdempotent(new Request(null, null, "/b1", null, null, null, false)))
323         System.err.println("Sequence b1 failed");
324     if (!seq.isIdempotent(new Request(null, null, "/b2", null, null, null, false)))
325         System.err.println("Sequence b2 failed");
326     if (!seq.isIdempotent(new Request(null, null, "/b3", null, null, null, false)))
327         System.err.println("Sequence b3 failed");
328     if (seq.isIdempotent(new Request(null, null, "/b4", null, null, null, false)))
329         System.err.println("Sequence b4 failed");
330     if (!seq.isIdempotent(new Request(null, null, "/b5", null, null, null, false)))
331         System.err.println("Sequence b5 failed");
332     if (seq.isIdempotent(new Request(null, null, "/b6", null, null, null, false)))
333         System.err.println("Sequence b6 failed");
334     if (seq.isIdempotent(new Request(null, null, "/b7", null, null, null, false)))
335         System.err.println("Sequence b7 failed");
336
337     System.out.println("Tests finished");
338     }
339 }
340
341
Popular Tags