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 */
022 package groovyx.net.http;
023
024 import java.io.IOException;
025 import java.net.URISyntaxException;
026 import java.util.Map;
027
028 import org.apache.http.HttpResponse;
029 import org.apache.http.client.ClientProtocolException;
030 import org.apache.http.client.methods.HttpDelete;
031 import org.apache.http.client.methods.HttpGet;
032 import org.apache.http.client.methods.HttpHead;
033 import org.apache.http.client.methods.HttpOptions;
034 import org.apache.http.client.methods.HttpPost;
035 import org.apache.http.client.methods.HttpPut;
036
037 /**
038 * Extension to HTTPBuilder that basically attempts to provide a slightly more
039 * REST-ful face on top of HTTPBuilder. The differences between this class
040 * and HTTPBuilder are such:
041 *
042 * <ul>
043 * <li>Access to response headers. All "request" methods on this class by
044 * default return an instance of {@link HttpResponseDecorator}, which allows for simple
045 * evaluation of the response.</li>
046 * <li>No streaming responses. Responses are expected to either not carry data
047 * (in the case of HEAD or DELETE) or be parse-able into some sort of object.
048 * That object is accessible via {@link HttpResponseDecorator#getData()}.</li>
049 * </ul>
050 *
051 * <p>By default, all request method methods will return a {@link HttpResponseDecorator}
052 * instance, which provides convenient access to response headers and the parsed
053 * response body. The response body is parsed based on content-type, identical
054 * to how HTTPBuilder's {@link HTTPBuilder#defaultSuccessHandler(HttpResponseDecorator,
055 * Object) default response handler} functions.</p>
056 *
057 * <p>Failed requests (i.e. responses which return a status code > 399) will
058 * by default throw a {@link HttpResponseException}. This exception may be used
059 * to retrieve additional information regarding the response as well.</p>
060 *
061 * @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
062 * @since 0.5
063 */
064 public class RESTClient extends HTTPBuilder {
065
066
067 /**
068 * Constructor.
069 * @see HTTPBuilder#HTTPBuilder()
070 */
071 public RESTClient() { super(); }
072
073 /**
074 * See {@link HTTPBuilder#HTTPBuilder(Object)}
075 * @param defaultURI default request URI (String, URI, URL or {@link URIBuilder})
076 * @throws URISyntaxException
077 */
078 public RESTClient( Object defaultURI ) throws URISyntaxException {
079 super( defaultURI );
080 }
081
082 /**
083 * See {@link HTTPBuilder#HTTPBuilder(Object, Object)}
084 * @param defaultURI default request URI (String, URI, URL or {@link URIBuilder})
085 * @param defaultContentType default content-type (String or {@link ContentType})
086 * @throws URISyntaxException
087 */
088 public RESTClient( Object defaultURI, Object defaultContentType ) throws URISyntaxException {
089 super( defaultURI, defaultContentType );
090 }
091
092
093 /**
094 * <p>Convenience method to perform an HTTP GET request. It will use the HTTPBuilder's
095 * {@link #getHandler() registered response handlers} to handle success or
096 * failure status codes. By default, the
097 * {@link #defaultSuccessHandler(HttpResponseDecorator, Object)}
098 * <code>success</code> response handler will return a decorated response
099 * object that can be used to read response headers and data.</p>
100 *
101 * <p>A 'failed' response (i.e. any HTTP status code > 399) will be handled
102 * by the registered 'failure' handler.
103 * The {@link #defaultFailureHandler(HttpResponseDecorator, Object)
104 * default failure handler} throws a {@link HttpResponseException}.</p>
105 *
106 * @see #defaultSuccessHandler(HttpResponseDecorator, Object)
107 * @see #defaultFailureHandler(HttpResponseDecorator, Object)
108 * @param args named parameters - see
109 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
110 * @return a {@link HttpResponseDecorator}, unless the default success
111 * handler is overridden.
112 * @throws URISyntaxException
113 * @throws IOException
114 * @throws ClientProtocolException
115 */
116 public Object get( Map<String,?> args ) throws ClientProtocolException,
117 IOException, URISyntaxException {
118 return super.doRequest( new RequestConfigDelegate( args, new HttpGet(), null ) );
119 }
120
121 /**
122 * <p>Convenience method to perform a POST request.</p>
123 *
124 * <p>The request body (specified by a <code>body</code> named parameter)
125 * will be encoded based on the <code>requestContentType</code> named
126 * parameter, or if none is given, the default
127 * {@link HTTPBuilder#setContentType(Object) content-type} for this instance.
128 * </p>
129 *
130 * @param args named parameters - see
131 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
132 * @return a {@link HttpResponseDecorator}, unless the default success
133 * handler is overridden.
134 * @throws ClientProtocolException
135 * @throws IOException
136 * @throws URISyntaxException
137 */
138 @Override public Object post( Map<String,?> args )
139 throws URISyntaxException, ClientProtocolException, IOException {
140 return super.doRequest( new RequestConfigDelegate( args, new HttpPost(), null ) );
141 }
142
143 /**
144 * <p> Convenience method to perform a PUT request.</p>
145 *
146 * <p>The request body (specified by a <code>body</code> named parameter)
147 * will be encoded based on the <code>requestContentType</code> named
148 * parameter, or if none is given, the default
149 * {@link HTTPBuilder#setContentType(Object) content-type} for this instance.
150 * </p>
151 *
152 * @param args named parameters - see
153 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
154 * @return a {@link HttpResponseDecorator}, unless the default success
155 * handler is overridden.
156 * @throws ClientProtocolException
157 * @throws IOException
158 * @throws URISyntaxException
159 */
160 public Object put( Map<String,?> args ) throws URISyntaxException,
161 ClientProtocolException, IOException {
162 return this.doRequest( new RequestConfigDelegate( args, new HttpPut(), null ) );
163 }
164
165 /**
166 * <p>Perform a HEAD request, often used to check preconditions before
167 * sending a large PUT or POST request.</p>
168 *
169 * @param args named parameters - see
170 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
171 * @return a {@link HttpResponseDecorator}, unless the default success
172 * handler is overridden.
173 * @throws ClientProtocolException
174 * @throws IOException
175 * @throws URISyntaxException
176 */
177 public Object head( Map<String,?> args ) throws URISyntaxException,
178 ClientProtocolException, IOException {
179 return this.doRequest( new RequestConfigDelegate( args, new HttpHead(), null ) );
180 }
181
182 /**
183 * <p>Perform a DELETE request. This method does not accept a
184 * <code>body</code> argument.</p>
185 *
186 * @param args named parameters - see
187 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
188 * @return a {@link HttpResponseDecorator}, unless the default success
189 * handler is overridden.
190 * @throws ClientProtocolException
191 * @throws IOException
192 * @throws URISyntaxException
193 */
194 public Object delete( Map<String,?> args ) throws URISyntaxException,
195 ClientProtocolException, IOException {
196 return this.doRequest( new RequestConfigDelegate( args, new HttpDelete(), null ) );
197 }
198
199 /**
200 * <p>Perform an OPTIONS request.</p>
201 *
202 * @param args named parameters - see
203 * {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
204 * @return a {@link HttpResponseDecorator}, unless the default success
205 * handler is overridden.
206 * @throws ClientProtocolException
207 * @throws IOException
208 * @throws URISyntaxException
209 */
210 public Object options( Map<String,?> args ) throws ClientProtocolException,
211 IOException, URISyntaxException {
212 return this.doRequest( new RequestConfigDelegate( args, new HttpOptions(), null ) );
213 }
214
215 /**
216 * Returns an {@link HttpResponseDecorator}, which provides simplified
217 * access to headers, response code, and parsed response body, as well as
218 * the underlying {@link HttpResponse} instance.
219 */
220 @Override
221 protected HttpResponseDecorator defaultSuccessHandler( HttpResponseDecorator resp, Object data )
222 throws ResponseParseException {
223 resp.setData( super.defaultSuccessHandler( resp, data ) );
224 return resp;
225 }
226
227 /**
228 * Throws an exception for non-successful HTTP response codes. The
229 * exception instance will have a reference to the response object, in
230 * order to inspect status code and headers within the <code>catch</code>
231 * block.
232 * @param resp response object
233 * @param data parsed response data
234 * @throws HttpResponseException exception which can access the response
235 * object.
236 */
237 protected void defaultFailureHandler( HttpResponseDecorator resp, Object data )
238 throws HttpResponseException {
239 resp.setData( super.defaultSuccessHandler( resp, data ) );
240 throw new HttpResponseException( resp );
241 }
242 }