/*
* Copyright 2008-2011 Thomas Nichols. http://blog.thomnichols.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* You are receiving this code free of charge, which represents many hours of
* effort from other individuals and corporations. As a responsible member
* of the community, you are encouraged (but not required) to donate any
* enhancements or improvements back to the community under a similar open
* source license. Thank you. -TMN
*/
package groovyx.net.http;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpPatch;
/**
* Extension to HTTPBuilder that basically attempts to provide a slightly more
* REST-ful face on top of HTTPBuilder. The differences between this class
* and HTTPBuilder are such:
*
* <ul>
* <li>Access to response headers. All "request" methods on this class by
* default return an instance of {@link HttpResponseDecorator}, which allows for simple
* evaluation of the response.</li>
* <li>No streaming responses. Responses are expected to either not carry data
* (in the case of HEAD or DELETE) or be parse-able into some sort of object.
* That object is accessible via {@link HttpResponseDecorator#getData()}.</li>
* </ul>
*
* <p>By default, all request method methods will return a {@link HttpResponseDecorator}
* instance, which provides convenient access to response headers and the parsed
* response body. The response body is parsed based on content-type, identical
* to how HTTPBuilder's {@link HTTPBuilder#defaultSuccessHandler(HttpResponseDecorator,
* Object) default response handler} functions.</p>
*
* <p>Failed requests (i.e. responses which return a status code > 399) will
* by default throw a {@link HttpResponseException}. This exception may be used
* to retrieve additional information regarding the response as well.</p>
*
* @author <a href='mailto:tomstrummer+httpbuilder@gmail.com'>Tom Nichols</a>
* @since 0.5
*/
public class RESTClient extends HTTPBuilder {
/**
* Constructor.
* @see HTTPBuilder#HTTPBuilder()
*/
public RESTClient() { super(); }
/**
* See {@link HTTPBuilder#HTTPBuilder(Object)}
* @param defaultURI default request URI (String, URI, URL or {@link URIBuilder})
* @throws URISyntaxException
*/
public RESTClient( Object defaultURI ) throws URISyntaxException {
super( defaultURI );
}
/**
* See {@link HTTPBuilder#HTTPBuilder(Object, Object)}
* @param defaultURI default request URI (String, URI, URL or {@link URIBuilder})
* @param defaultContentType default content-type (String or {@link ContentType})
* @throws URISyntaxException
*/
public RESTClient( Object defaultURI, Object defaultContentType ) throws URISyntaxException {
super( defaultURI, defaultContentType );
}
/**
* <p>Convenience method to perform an HTTP GET request. It will use the HTTPBuilder's
* {@link #getHandler() registered response handlers} to handle success or
* failure status codes. By default, the
* {@link #defaultSuccessHandler(HttpResponseDecorator, Object)}
* <code>success</code> response handler will return a decorated response
* object that can be used to read response headers and data.</p>
*
* <p>A 'failed' response (i.e. any HTTP status code > 399) will be handled
* by the registered 'failure' handler.
* The {@link #defaultFailureHandler(HttpResponseDecorator, Object)
* default failure handler} throws a {@link HttpResponseException}.</p>
*
* @see #defaultSuccessHandler(HttpResponseDecorator, Object)
* @see #defaultFailureHandler(HttpResponseDecorator, Object)
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws URISyntaxException
* @throws IOException
* @throws ClientProtocolException
*/
public Object get( Map<String,?> args ) throws ClientProtocolException,
IOException, URISyntaxException {
return doRequest( new RequestConfigDelegate( args, new HttpGet(), null ) );
}
/**
* <p>Convenience method to perform a POST request.</p>
*
* <p>The request body (specified by a <code>body</code> named parameter)
* will be encoded based on the <code>requestContentType</code> named
* parameter, or if none is given, the default
* {@link HTTPBuilder#setContentType(Object) content-type} for this instance.
* </p>
*
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws ClientProtocolException
* @throws IOException
* @throws URISyntaxException
*/
@Override public Object post( Map<String,?> args )
throws URISyntaxException, ClientProtocolException, IOException {
return doRequest( new RequestConfigDelegate( args, new HttpPost(), null ) );
}
/**
* <p> Convenience method to perform a PUT request.</p>
*
* <p>The request body (specified by a <code>body</code> named parameter)
* will be encoded based on the <code>requestContentType</code> named
* parameter, or if none is given, the default
* {@link HTTPBuilder#setContentType(Object) content-type} for this instance.
* </p>
*
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws ClientProtocolException
* @throws IOException
* @throws URISyntaxException
*/
public Object put( Map<String,?> args ) throws URISyntaxException,
ClientProtocolException, IOException {
return this.doRequest( new RequestConfigDelegate( args, new HttpPut(), null ) );
}
/**
* <p> Convenience method to perform a PATCH request.</p>
*
* <p>The request body (specified by a <code>body</code> named parameter)
* will be encoded based on the <code>requestContentType</code> named
* parameter, or if none is given, the default
* {@link HTTPBuilder#setContentType(Object) content-type} for this instance.
* </p>
*
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws ClientProtocolException
* @throws IOException
* @throws URISyntaxException
*/
public Object patch( Map<String,?> args ) throws URISyntaxException,
ClientProtocolException, IOException {
return this.doRequest( new RequestConfigDelegate( args, new HttpPatch(), null ) );
}
/**
* <p>Perform a HEAD request, often used to check preconditions before
* sending a large PUT or POST request.</p>
*
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws ClientProtocolException
* @throws IOException
* @throws URISyntaxException
*/
public Object head( Map<String,?> args ) throws URISyntaxException,
ClientProtocolException, IOException {
return this.doRequest( new RequestConfigDelegate( args, new HttpHead(), null ) );
}
/**
* <p>Perform a DELETE request. This method does not accept a
* <code>body</code> argument.</p>
*
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws ClientProtocolException
* @throws IOException
* @throws URISyntaxException
*/
public Object delete( Map<String,?> args ) throws URISyntaxException,
ClientProtocolException, IOException {
return this.doRequest( new RequestConfigDelegate( args, new HttpDelete(), null ) );
}
/**
* <p>Perform an OPTIONS request.</p>
*
* @param args named parameters - see
* {@link HTTPBuilder.RequestConfigDelegate#setPropertiesFromMap(Map)}
* @return a {@link HttpResponseDecorator}, unless the default success
* handler is overridden.
* @throws ClientProtocolException
* @throws IOException
* @throws URISyntaxException
*/
public Object options( Map<String,?> args ) throws ClientProtocolException,
IOException, URISyntaxException {
return this.doRequest( new RequestConfigDelegate( args, new HttpOptions(), null ) );
}
/**
* Returns an {@link HttpResponseDecorator}, which provides simplified
* access to headers, response code, and parsed response body, as well as
* the underlying {@link HttpResponse} instance.
*/
@Override
protected HttpResponseDecorator defaultSuccessHandler( HttpResponseDecorator resp, Object data )
throws ResponseParseException {
resp.setData( super.defaultSuccessHandler( resp, data ) );
return resp;
}
/**
* Throws an exception for non-successful HTTP response codes. The
* exception instance will have a reference to the response object, in
* order to inspect status code and headers within the <code>catch</code>
* block.
* @param resp response object
* @param data parsed response data
* @throws HttpResponseException exception which can access the response
* object.
*/
protected void defaultFailureHandler( HttpResponseDecorator resp, Object data )
throws HttpResponseException {
resp.setData( super.defaultSuccessHandler( resp, data ) );
throw new HttpResponseException( resp );
}
}