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

Java Develop

Development Platform:

Java

  1. /*
  2.  * @(#)RetryModule.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.io.IOException;
  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-3  06/05/2001
  46.  * @author Ronald Tschal鋜
  47.  * @since V0.3
  48.  */
  49. class RetryModule implements HTTPClientModule, GlobalConstants
  50. {
  51.     // Constructors
  52.     /**
  53.      */
  54.     RetryModule()
  55.     {
  56.     }
  57.     // Methods
  58.     /**
  59.      * Invoked by the HTTPClient.
  60.      */
  61.     public int requestHandler(Request req, Response[] resp)
  62.     {
  63. return REQ_CONTINUE;
  64.     }
  65.     /**
  66.      * Invoked by the HTTPClient.
  67.      */
  68.     public void responsePhase1Handler(Response resp, RoRequest roreq)
  69.     throws IOException, ModuleException
  70.     {
  71. try
  72. {
  73.     resp.getStatusCode();
  74. }
  75. catch (RetryException re)
  76. {
  77.     Log.write(Log.MODS, "RtryM: Caught RetryException");
  78.     boolean got_lock = false;
  79.     try
  80.     {
  81.     synchronized (re.first)
  82.     {
  83. got_lock = true;
  84. // initialize idempotent sequence checking
  85. IdempotentSequence seq = new IdempotentSequence();
  86. for (RetryException e=re.first; e!=null; e=e.next)
  87.     seq.add(e.request);
  88. for (RetryException e=re.first; e!=null; e=e.next)
  89. {
  90.     Log.write(Log.MODS, "RtryM: handling exception ", e);
  91.     Request req = e.request;
  92.     HTTPConnection con = req.getConnection();
  93.     /* Don't retry if either the sequence is not idempotent
  94.      * (Sec 8.1.4 and 9.1.2), or we've already retried enough
  95.      * times, or the headers have been read and parsed
  96.      * already
  97.      */
  98.     if (!seq.isIdempotent(req)  ||
  99. (con.ServProtVersKnown  &&
  100.  con.ServerProtocolVersion >= HTTP_1_1  &&
  101.  req.num_retries > 0)  ||
  102. ((!con.ServProtVersKnown  ||
  103.   con.ServerProtocolVersion <= HTTP_1_0)  &&
  104.  req.num_retries > 4)  ||
  105. e.response.got_headers)
  106.     {
  107. e.first = null;
  108. continue;
  109.     }
  110.     /**
  111.      * if an output stream was used (i.e. we don't have the
  112.      * data to resend) then delegate the responsibility for
  113.      * resending to the application.
  114.      */
  115.     if (req.getStream() != null)
  116.     {
  117. if (HTTPConnection.deferStreamed)
  118. {
  119.     req.getStream().reset();
  120.     e.response.setRetryRequest(true);
  121. }
  122. e.first = null;
  123. continue;
  124.     }
  125.     /* If we have an entity then setup either the entity-delay
  126.      * or the Expect header
  127.      */
  128.     if (req.getData() != null  &&  e.conn_reset)
  129.     {
  130. if (con.ServProtVersKnown  &&
  131.     con.ServerProtocolVersion >= HTTP_1_1)
  132.     addToken(req, "Expect", "100-continue");
  133. else
  134.     req.delay_entity = 5000L << req.num_retries;
  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.     /* If this an HTTP/1.1 server then don't pipeline retries.
  150.      * The problem is that if the server for some reason
  151.      * decides not to use persistent connections and it does
  152.      * not do a correct shutdown of the connection, then the
  153.      * response will be ReSeT. If we did pipeline then we
  154.      * would keep falling into this trap indefinitely.
  155.      *
  156.      * Note that for HTTP/1.0 servers, if they don't support
  157.      * keep-alives then the normal code will already handle
  158.      * this accordingly and won't pipe over the same
  159.      * connection.
  160.      */
  161.     if (con.ServProtVersKnown  &&
  162. con.ServerProtocolVersion >= HTTP_1_1  &&
  163. e.conn_reset)
  164.     {
  165. req.dont_pipeline = true;
  166.     }
  167.     // The above is too risky - for moment let's be safe
  168.     // and never pipeline retried request at all.
  169.     req.dont_pipeline = true;
  170.     // now resend the request
  171.     Log.write(Log.MODS, "RtryM: Retrying request '" +
  172. req.getMethod() + " " +
  173. req.getRequestURI() + "'");
  174.     if (e.conn_reset)
  175. req.num_retries++;
  176.     e.response.http_resp.set(req,
  177.     con.sendRequest(req, e.response.timeout));
  178.     e.exception = null;
  179.     e.first = null;
  180. }
  181.     }
  182.     }
  183.     catch (NullPointerException npe)
  184. { if (got_lock)  throw npe; }
  185.     catch (ParseException pe)
  186. { throw new IOException(pe.getMessage()); }
  187.     if (re.exception != null)  throw re.exception;
  188.     re.restart = true;
  189.     throw re;
  190. }
  191.     }
  192.     /**
  193.      * Invoked by the HTTPClient.
  194.      */
  195.     public int responsePhase2Handler(Response resp, Request req)
  196.     {
  197. // reset any stuff we might have set previously
  198. req.delay_entity  = 0;
  199. req.dont_pipeline = false;
  200. req.num_retries   = 0;
  201. return RSP_CONTINUE;
  202.     }
  203.     /**
  204.      * Invoked by the HTTPClient.
  205.      */
  206.     public void responsePhase3Handler(Response resp, RoRequest req)
  207.     {
  208.     }
  209.     /**
  210.      * Invoked by the HTTPClient.
  211.      */
  212.     public void trailerHandler(Response resp, RoRequest req)
  213.     {
  214.     }
  215.     /**
  216.      * Add a token to the given header. If the header does not exist then
  217.      * create it with the given token.
  218.      *
  219.      * @param req the request who's headers are to be modified
  220.      * @param hdr the name of the header to add the token to (or to create)
  221.      * @param tok the token to add
  222.      * @exception ParseException if parsing the header fails
  223.      */
  224.     private void addToken(Request req, String hdr, String tok)
  225.     throws ParseException
  226.     {
  227. int idx;
  228. NVPair[] hdrs = req.getHeaders();
  229. for (idx=0; idx<hdrs.length; idx++)
  230. {
  231.     if (hdrs[idx].getName().equalsIgnoreCase(hdr))
  232. break;
  233. }
  234. if (idx == hdrs.length) // no such header, so add one
  235. {
  236.     hdrs = Util.resizeArray(hdrs, idx+1);
  237.     hdrs[idx] = new NVPair(hdr, tok);
  238.     req.setHeaders(hdrs);
  239. }
  240. else // header exists, so add token
  241. {
  242.     if (!Util.hasToken(hdrs[idx].getValue(), tok))
  243. hdrs[idx] = new NVPair(hdr, hdrs[idx].getValue() + ", " + tok);
  244. }
  245.     }
  246. }