KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > RetryModule


1 /*
2  * @(#)RetryModule.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 import java.io.IOException JavaDoc;
32
33
34 /**
35  * This module handles request retries when a connection closes prematurely.
36  * It is triggered by the RetryException thrown by the StreamDemultiplexor.
37  *
38  * <P>This module is somewhat unique in that it doesn't strictly limit itself
39  * to the HTTPClientModule interface and its return values. That is, it
40  * sends request directly using the HTTPConnection.sendRequest() method. This
41  * is necessary because this module will not only resend its request but it
42  * also resend all other requests in the chain. Also, it rethrows the
43  * RetryException in Phase1 to restart the processing of the modules.
44  *
45  * @version 0.3-2 18/06/1999
46  * @author Ronald Tschalär
47  * @since V0.3
48  */

49
50 class RetryModule implements HTTPClientModule, GlobalConstants
51 {
52     // Constructors
53

54     /**
55      */

56     RetryModule()
57     {
58     }
59
60
61     // Methods
62

63     /**
64      * Invoked by the HTTPClient.
65      */

66     public int requestHandler(Request req, Response[] resp)
67     {
68     return REQ_CONTINUE;
69     }
70
71
72     /**
73      * Invoked by the HTTPClient.
74      */

75     public void responsePhase1Handler(Response resp, RoRequest roreq)
76         throws IOException JavaDoc, ModuleException
77     {
78     try
79     {
80         resp.getStatusCode();
81     }
82     catch (RetryException re)
83     {
84         if (DebugMods) System.err.println("RtryM: Caught RetryException");
85
86         boolean got_lock = false;
87
88         try
89         {
90         synchronized (re.first)
91         {
92         got_lock = true;
93
94         // initialize idempotent sequence checking
95
IdempotentSequence seq = new IdempotentSequence();
96         for (RetryException e=re.first; e!=null; e=e.next)
97             seq.add(e.request);
98
99         for (RetryException e=re.first; e!=null; e=e.next)
100         {
101             Request req = e.request;
102             HTTPConnection con = req.getConnection();
103
104             /* Don't retry if either the sequence is not idempotent
105              * (Sec 8.1.4 and 9.1.2), or we've already retried enough
106              * times, or the headers have been read and parsed
107              * already
108              */

109             if (!seq.isIdempotent(req) ||
110             (con.ServProtVersKnown &&
111              con.ServerProtocolVersion >= HTTP_1_1 &&
112              req.num_retries > 0) ||
113             ((!con.ServProtVersKnown ||
114               con.ServerProtocolVersion <= HTTP_1_0) &&
115              req.num_retries > 4) ||
116             e.response.got_headers ||
117             req.getStream() != null)
118             {
119             e.first = null;
120             continue;
121             }
122
123
124             /* If we have an entity then setup either the entity-delay
125              * or the Expect header
126              */

127             if (req.getData() != null && e.conn_reset)
128             {
129             if (con.ServProtVersKnown &&
130                 con.ServerProtocolVersion >= HTTP_1_1)
131                 addToken(req, "Expect", "100-continue");
132             else
133                 req.delay_entity = 5000L << req.num_retries;
134             }
135
136             /* If the next request in line has an entity and we're
137              * talking to an HTTP/1.0 server then close the socket
138              * after this request. This is so that the available()
139              * call (to watch for an error response from the server)
140              * will work correctly.
141              */

142             if (e.next != null && e.next.request.getData() != null &&
143             (!con.ServProtVersKnown ||
144              con.ServerProtocolVersion < HTTP_1_1) &&
145              e.conn_reset)
146             {
147             addToken(req, "Connection", "close");
148             }
149
150
151             /* If this an HTTP/1.1 server then don't pipeline retries.
152              * The problem is that if the server for some reason
153              * decides not to use persistent connections and it does
154              * not do a correct shutdown of the connection, then the
155              * response will be ReSeT. If we did pipeline then we
156              * would keep falling into this trap indefinitely.
157              *
158              * Note that for HTTP/1.0 servers, if they don't support
159              * keep-alives then the normal code will already handle
160              * this accordingly and won't pipe over the same
161              * connection.
162              */

163             if (con.ServProtVersKnown &&
164             con.ServerProtocolVersion >= HTTP_1_1 &&
165             e.conn_reset)
166             {
167             req.dont_pipeline = true;
168             }
169             // The above is too risky - for moment let's be safe
170
// and never pipeline retried request at all.
171
req.dont_pipeline = true;
172
173
174             // now resend the request
175

176             if (DebugDemux)
177             System.err.println("RtryM: Retrying request '" +
178                         req.getMethod() + " " +
179                         req.getRequestURI() + "'");
180
181             if (e.conn_reset)
182             req.num_retries++;
183             e.response.http_resp.set(req,
184                     con.sendRequest(req, e.response.timeout));
185             e.exception = null;
186             e.first = null;
187         }
188         }
189         }
190         catch (NullPointerException JavaDoc npe)
191         { if (got_lock) throw npe; }
192         catch (ParseException pe)
193         { throw new IOException JavaDoc(pe.getMessage()); }
194
195         if (re.exception != null) throw re.exception;
196
197         re.restart = true;
198         throw re;
199     }
200     }
201
202
203     /**
204      * Invoked by the HTTPClient.
205      */

206     public int responsePhase2Handler(Response resp, Request req)
207     {
208     // reset any stuff we might have set previously
209
req.delay_entity = 0;
210     req.dont_pipeline = false;
211     req.num_retries = 0;
212
213     return RSP_CONTINUE;
214     }
215
216
217     /**
218      * Invoked by the HTTPClient.
219      */

220     public void responsePhase3Handler(Response resp, RoRequest req)
221     {
222     }
223
224
225     /**
226      * Invoked by the HTTPClient.
227      */

228     public void trailerHandler(Response resp, RoRequest req)
229     {
230     }
231
232
233     /**
234      * Add a token to the given header. If the header does not exist then
235      * create it with the given token.
236      *
237      * @param req the request who's headers are to be modified
238      * @param hdr the name of the header to add the token to (or to create)
239      * @param tok the token to add
240      * @exception ParseException if parsing the header fails
241      */

242     private void addToken(Request req, String JavaDoc hdr, String JavaDoc tok)
243         throws ParseException
244     {
245     int idx;
246     NVPair[] hdrs = req.getHeaders();
247     for (idx=0; idx<hdrs.length; idx++)
248     {
249         if (hdrs[idx].getName().equalsIgnoreCase(hdr))
250         break;
251     }
252
253     if (idx == hdrs.length) // no such header, so add one
254
{
255         hdrs = Util.resizeArray(hdrs, idx+1);
256         hdrs[idx] = new NVPair(hdr, tok);
257         req.setHeaders(hdrs);
258     }
259     else // header exists, so add token
260
{
261         if (!Util.hasToken(hdrs[idx].getValue(), tok))
262         hdrs[idx] = new NVPair(hdr, hdrs[idx].getValue() + ", " + tok);
263     }
264     }
265 }
266
267
Popular Tags