001/* 002 * Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * You are receiving this code free of charge, which represents many hours of 017 * effort from other individuals and corporations. As a responsible member 018 * of the community, you are encouraged (but not required) to donate any 019 * enhancements or improvements back to the community under a similar open 020 * source license. Thank you. -TMN 021 */ 022package groovyx.net.http; 023 024import java.io.IOException; 025import java.net.URISyntaxException; 026import java.util.Map; 027 028import org.apache.http.HttpResponse; 029import org.apache.http.client.ClientProtocolException; 030import org.apache.http.client.methods.HttpDelete; 031import org.apache.http.client.methods.HttpGet; 032import org.apache.http.client.methods.HttpHead; 033import org.apache.http.client.methods.HttpOptions; 034import org.apache.http.client.methods.HttpPost; 035import org.apache.http.client.methods.HttpPut; 036import org.apache.http.client.methods.HttpPatch; 037 038/** 039 * Extension to HTTPBuilder that basically attempts to provide a slightly more 040 * REST-ful face on top of HTTPBuilder. The differences between this class 041 * and HTTPBuilder are such: 042 * 043 * <ul> 044 * <li>Access to response headers. All "request" methods on this class by 045 * default return an instance of {@link HttpResponseDecorator}, which allows for simple 046 * evaluation of the response.</li> 047 * <li>No streaming responses. Responses are expected to either not carry data 048 * (in the case of HEAD or DELETE) or be parse-able into some sort of object. 049 * That object is accessible via {@link HttpResponseDecorator#getData()}.</li> 050 * </ul> 051 * 052 * <p>By default, all request method methods will return a {@link HttpResponseDecorator} 053 * instance, which provides convenient access to response headers and the parsed 054 * response body. The response body is parsed based on content-type, identical 055 * to how HTTPBuilder's {@link HTTPBuilder#defaultSuccessHandler(HttpResponseDecorator, 056 * Object) default response handler} functions.</p> 057 * 058 * <p>Failed requests (i.e. responses which return a status code > 399) will 059 * by default throw a {@link HttpResponseException}. This exception may be used 060 * to retrieve additional information regarding the response as well.</p> 061 * 062 * @author <a href='mailto:[email protected]'>Tom Nichols</a> 063 * @since 0.5 064 */ 065public class RESTClient extends HTTPBuilder { 066 067 068 /** 069 * Constructor. 070 * @see HTTPBuilder#HTTPBuilder() 071 */ 072 public RESTClient() { super(); } 073 074 /** 075 * See {@link HTTPBuilder#HTTPBuilder(Object)} 076 * @param defaultURI default request URI (String, URI, URL or {@link URIBuilder}) 077 * @throws URISyntaxException 078 */ 079 public RESTClient( Object defaultURI ) throws URISyntaxException { 080 super( defaultURI ); 081 } 082 083 /** 084 * See {@link HTTPBuilder#HTTPBuilder(Object, Object)} 085 * @param defaultURI default request URI (String, URI, URL or {@link URIBuilder}) 086 * @param defaultContentType default content-type (String or {@link ContentType}) 087 * @throws URISyntaxException 088 */ 089 public RESTClient( Object defaultURI, Object defaultContentType ) throws URISyntaxException { 090 super( defaultURI, defaultContentType ); 091 } 092 093 094 /** 095 * <p>Convenience method to perform an HTTP GET request. It will use the HTTPBuilder's 096 * {@link #getHandler() registered response handlers} to handle success or 097 * failure status codes. By default, the 098 * {@link #defaultSuccessHandler(HttpResponseDecorator, Object)} 099 * <code>success</code> response handler will return a decorated response 100 * object that can be used to read response headers and data.</p> 101 * 102 * <p>A 'failed' response (i.e. any HTTP status code > 399) will be handled 103 * by the registered 'failure' handler. 104 * The {@link #defaultFailureHandler(HttpResponseDecorator, Object) 105 * default failure handler} throws a {@link HttpResponseException}.</p> 106 * 107 * @see #defaultSuccessHandler(HttpResponseDecorator, Object) 108 * @see #defaultFailureHandler(HttpResponseDecorator, Object) 109 * @param args named parameters - see 110 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 111 * @return a {@link HttpResponseDecorator}, unless the default success 112 * handler is overridden. 113 * @throws URISyntaxException 114 * @throws IOException 115 * @throws ClientProtocolException 116 */ 117 public Object get( Map<String,?> args ) throws ClientProtocolException, 118 IOException, URISyntaxException { 119 return super.doRequest( new RequestConfigDelegate( args, new HttpGet(), null ) ); 120 } 121 122 /** 123 * <p>Convenience method to perform a POST request.</p> 124 * 125 * <p>The request body (specified by a <code>body</code> named parameter) 126 * will be encoded based on the <code>requestContentType</code> named 127 * parameter, or if none is given, the default 128 * {@link HTTPBuilder#setContentType(Object) content-type} for this instance. 129 * </p> 130 * 131 * @param args named parameters - see 132 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 133 * @return a {@link HttpResponseDecorator}, unless the default success 134 * handler is overridden. 135 * @throws ClientProtocolException 136 * @throws IOException 137 * @throws URISyntaxException 138 */ 139 @Override public Object post( Map<String,?> args ) 140 throws URISyntaxException, ClientProtocolException, IOException { 141 return super.doRequest( new RequestConfigDelegate( args, new HttpPost(), null ) ); 142 } 143 144 /** 145 * <p> Convenience method to perform a PUT request.</p> 146 * 147 * <p>The request body (specified by a <code>body</code> named parameter) 148 * will be encoded based on the <code>requestContentType</code> named 149 * parameter, or if none is given, the default 150 * {@link HTTPBuilder#setContentType(Object) content-type} for this instance. 151 * </p> 152 * 153 * @param args named parameters - see 154 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 155 * @return a {@link HttpResponseDecorator}, unless the default success 156 * handler is overridden. 157 * @throws ClientProtocolException 158 * @throws IOException 159 * @throws URISyntaxException 160 */ 161 public Object put( Map<String,?> args ) throws URISyntaxException, 162 ClientProtocolException, IOException { 163 return this.doRequest( new RequestConfigDelegate( args, new HttpPut(), null ) ); 164 } 165 166 /** 167 * <p> Convenience method to perform a PATCH request.</p> 168 * 169 * <p>The request body (specified by a <code>body</code> named parameter) 170 * will be encoded based on the <code>requestContentType</code> named 171 * parameter, or if none is given, the default 172 * {@link HTTPBuilder#setContentType(Object) content-type} for this instance. 173 * </p> 174 * 175 * @param args named parameters - see 176 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 177 * @return a {@link HttpResponseDecorator}, unless the default success 178 * handler is overridden. 179 * @throws ClientProtocolException 180 * @throws IOException 181 * @throws URISyntaxException 182 */ 183 public Object patch( Map<String,?> args ) throws URISyntaxException, 184 ClientProtocolException, IOException { 185 return this.doRequest( new RequestConfigDelegate( args, new HttpPatch(), null ) ); 186 } 187 188 /** 189 * <p>Perform a HEAD request, often used to check preconditions before 190 * sending a large PUT or POST request.</p> 191 * 192 * @param args named parameters - see 193 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 194 * @return a {@link HttpResponseDecorator}, unless the default success 195 * handler is overridden. 196 * @throws ClientProtocolException 197 * @throws IOException 198 * @throws URISyntaxException 199 */ 200 public Object head( Map<String,?> args ) throws URISyntaxException, 201 ClientProtocolException, IOException { 202 return this.doRequest( new RequestConfigDelegate( args, new HttpHead(), null ) ); 203 } 204 205 /** 206 * <p>Perform a DELETE request. This method does not accept a 207 * <code>body</code> argument.</p> 208 * 209 * @param args named parameters - see 210 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 211 * @return a {@link HttpResponseDecorator}, unless the default success 212 * handler is overridden. 213 * @throws ClientProtocolException 214 * @throws IOException 215 * @throws URISyntaxException 216 */ 217 public Object delete( Map<String,?> args ) throws URISyntaxException, 218 ClientProtocolException, IOException { 219 return this.doRequest( new RequestConfigDelegate( args, new HttpDelete(), null ) ); 220 } 221 222 /** 223 * <p>Perform an OPTIONS request.</p> 224 * 225 * @param args named parameters - see 226 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)} 227 * @return a {@link HttpResponseDecorator}, unless the default success 228 * handler is overridden. 229 * @throws ClientProtocolException 230 * @throws IOException 231 * @throws URISyntaxException 232 */ 233 public Object options( Map<String,?> args ) throws ClientProtocolException, 234 IOException, URISyntaxException { 235 return this.doRequest( new RequestConfigDelegate( args, new HttpOptions(), null ) ); 236 } 237 238 /** 239 * Returns an {@link HttpResponseDecorator}, which provides simplified 240 * access to headers, response code, and parsed response body, as well as 241 * the underlying {@link HttpResponse} instance. 242 */ 243 @Override 244 protected HttpResponseDecorator defaultSuccessHandler( HttpResponseDecorator resp, Object data ) 245 throws ResponseParseException { 246 resp.setData( super.defaultSuccessHandler( resp, data ) ); 247 return resp; 248 } 249 250 /** 251 * Throws an exception for non-successful HTTP response codes. The 252 * exception instance will have a reference to the response object, in 253 * order to inspect status code and headers within the <code>catch</code> 254 * block. 255 * @param resp response object 256 * @param data parsed response data 257 * @throws HttpResponseException exception which can access the response 258 * object. 259 */ 260 protected void defaultFailureHandler( HttpResponseDecorator resp, Object data ) 261 throws HttpResponseException { 262 resp.setData( super.defaultSuccessHandler( resp, data ) ); 263 throw new HttpResponseException( resp ); 264 } 265}