View Javadoc

1   /* $Id: HttpServiceClient.java,v 1.5 2004/09/14 16:26:26 jdt Exp $
2    * Created on Jul 24, 2004
3    * Copyright (C) 2004 AstroGrid. All rights reserved.
4    *
5    * This software is published under the terms of the AstroGrid 
6    * Software License version 1.2, a copy of which has been included 
7    * with this distribution in the LICENSE.txt file.  
8    */
9   package org.astrogrid.applications.http;
10  
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  
14  import java.io.IOException;
15  import java.net.MalformedURLException;
16  import java.util.Iterator;
17  import java.util.Map;
18  import java.util.Set;
19  
20  import org.apache.commons.httpclient.HttpClient;
21  import org.apache.commons.httpclient.HttpException;
22  import org.apache.commons.httpclient.HttpMethod;
23  import org.apache.commons.httpclient.HttpRecoverableException;
24  import org.apache.commons.httpclient.NameValuePair;
25  import org.apache.commons.httpclient.URIException;
26  import org.apache.commons.httpclient.methods.GetMethod;
27  import org.apache.commons.httpclient.methods.PostMethod;
28  import org.astrogrid.applications.http.exceptions.HttpApplicationNetworkException;
29  import org.astrogrid.applications.http.exceptions.HttpApplicationWebServiceURLException;
30  ;
31  
32  /***
33   * Talks to the http get or post service and post-processes the response.
34   * @TODO think about big results...streaming, char encoding etc
35   * @TODO how are we going to test?
36   * 
37   * @author jdt
38   */
39  public class HttpServiceClient {
40      /***
41       * Logger for this class
42       */
43      private static final Log log = LogFactory.getLog(HttpServiceClient.class);
44  
45      /***
46       * The get or set method to call on the server.
47       */
48      private HttpMethod method;
49      /***
50       * The client that processes the methods.  
51       * @TODO can we make this static?
52       */
53      private HttpClient client = new HttpClient();
54      /***
55       * Number of attempts we make before giving up.
56       * @TODO consider making this a property.
57       */
58      private static final int MAX_ATTEMPTS = 3;
59      private static final int NOT_FOUND = 404;
60  
61      /***
62       * Represents if the httpservice is a get or a post. java enum idiom
63       * 
64       * @author jdt
65       *  
66       */
67      public static class HttpServiceType {
68          /***
69           * Logger for this class
70           */
71          private static final Log log = LogFactory.getLog(HttpServiceType.class);
72  
73          public static HttpServiceType GET = new HttpServiceType("get");
74  
75          public static HttpServiceType POST = new HttpServiceType("post");
76          
77          public static HttpServiceType TEST = new HttpServiceType("mock");
78  
79          private String type;
80  
81          /***
82           * ctor
83           * 
84           * @param type name of the type
85           */
86          private HttpServiceType(final String type) {
87              this.type = type;
88          }
89  
90          public String toString() {
91              return type;
92          }
93      }
94  
95      /***
96       * Constructor
97       * Note - the constructor may throw an IllegalArgumentException
98       * if this URI is invalid.  Not my fault - that's what the underlying
99       * commons-http library does.
100      */
101     public HttpServiceClient(final String serviceUrl, final HttpServiceType serviceType) {
102         if (log.isTraceEnabled()) {
103             log.trace("HttpServiceClient(String serviceUrl = " + serviceUrl + ", HttpServiceType serviceType = "
104                     + serviceType + ") - start");
105         }
106 
107         if (serviceType==HttpServiceType.GET) {
108             method = new GetMethod(serviceUrl);
109         } else if (serviceType==HttpServiceType.POST) {
110             method = new PostMethod(serviceUrl);
111             //throw new UnsupportedOperationException("Post is not supported just yet");
112         } else if (serviceType==HttpServiceType.TEST){
113             throw new UnsupportedOperationException("Haven't done this yet");
114         }
115         assert method!=null : "serviceType should be get, post or test";
116 
117         if (log.isTraceEnabled()) {
118             log.trace("HttpServiceClient(String, HttpServiceType) - end");
119         }
120     }
121 
122     /***
123      * Call the web server and return stuff.
124      * @return
125      * @throws HttpApplicationNetworkException
126      * @throws URIException
127      * @throws HttpApplicationWebServiceURLException
128      * @throws IOException
129      * @throws HttpException
130      */
131     public String call(final Map args) throws HttpApplicationNetworkException, HttpApplicationWebServiceURLException {
132         if (log.isTraceEnabled()) {
133             log.trace("call(Map args = " + args + ") - start");
134         }
135 
136         //@TODO refactor this away
137         NameValuePair[] argsArray = new NameValuePair[args.size()];
138         Set keys = args.keySet();
139         Iterator it = keys.iterator();
140         for (int i=0; it.hasNext(); ++i) {
141             Object key = it.next();
142             argsArray[i] = new NameValuePair((String) key, (String) args.get(key));
143         }
144         
145         //Unfortunately, the GetMethod and PostMethods have different
146         //ways of setting the queries.  Using instanceof is ugly,
147         //but is the simplest way
148         if (method instanceof PostMethod) {
149         	((PostMethod)method).setRequestBody(argsArray);
150         } else {
151         	method.setQueryString(argsArray);
152         }
153         
154         int statusCode = -1;
155         // We will retry up to MAX_ATTEMPTS times.
156         String results = null;
157         for (int attempt = 0; statusCode == -1 && attempt < MAX_ATTEMPTS; attempt++) {
158             try {
159                 // execute the method.
160             	log.debug("Executing method: "+method);
161                 statusCode = client.executeMethod(method);
162                 results = method.getResponseBodyAsString();
163                 log.debug("Method returned with results: "+results);
164             } catch (HttpRecoverableException e) {
165                 log.warn("A recoverable error occured, retrying..."+attempt,e);
166             } catch (MalformedURLException e) {
167                 log.error(e);
168                 throw new HttpApplicationWebServiceURLException("A non-recoverable error occurred connecting to the web site", e);
169             } catch (IOException e) {
170                 log.error(e);
171                 throw new HttpApplicationNetworkException("A non-recoverable error occurred connecting to the web site", e);
172             } finally {
173             
174                 //@TODO will this fail if there *was* an exception?
175                 method.releaseConnection();
176                 //@TODO consider recycling.
177             }
178         }
179 
180         // Check that we didn't run out of retries.
181         if (statusCode == -1) {
182             log.error("Failed to connect");
183             throw new HttpApplicationNetworkException("Failed to execute after "+MAX_ATTEMPTS+" tries");
184         } else if (statusCode == NOT_FOUND) {
185             try {
186                 log.error("Web site not found");
187                 throw new HttpApplicationWebServiceURLException("Page not found at " + method.getURI());
188             } catch (URIException e) {
189                 // thrown by getURI method, strangely - no action needed
190                 log.error("Unknown URI");
191             }
192         }
193         
194         assert results!=null;
195 
196         if (log.isTraceEnabled()) {
197             log.trace("call(Map) - end - return value = " + results);
198         }
199         return results;
200     }
201 
202 }
203 
204 /*
205  * $Log: HttpServiceClient.java,v $
206  * Revision 1.5  2004/09/14 16:26:26  jdt
207  * Attempt to get the http-post working.  Upgraded http-client, to no avail.  Either http client
208  * or the embedded test webserver isn't handling post correctly.  Flagged tests
209  * to ensure this is dealt with.
210  *
211  * Revision 1.4  2004/09/01 15:42:26  jdt
212  * Merged in Case 3
213  *
214  * Revision 1.1.4.2  2004/08/11 22:55:35  jdt
215  * Refactoring, and a lot of new unit tests.
216  *
217  * Revision 1.1.4.1  2004/07/27 17:20:25  jdt
218  * merged from applications_JDT_case3
219  *
220  * Revision 1.1.2.3  2004/07/27 17:12:44  jdt
221  * refactored exceptions and finished tests for HttpServiceClient
222  *
223  * Revision 1.1.2.2  2004/07/26 16:20:30  jdt
224  * Added some tests.
225  *
226  * Revision 1.1.2.1  2004/07/24 18:43:29  jdt
227  * Started plumbing in the httpclient
228  *
229  */