001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package com.noelios.restlet.ext.httpclient;
020:
021: import java.io.FilterInputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.util.logging.Level;
026:
027: import org.apache.commons.httpclient.ConnectMethod;
028: import org.apache.commons.httpclient.Header;
029: import org.apache.commons.httpclient.HostConfiguration;
030: import org.apache.commons.httpclient.HttpMethod;
031: import org.apache.commons.httpclient.URI;
032: import org.apache.commons.httpclient.URIException;
033: import org.apache.commons.httpclient.methods.DeleteMethod;
034: import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
035: import org.apache.commons.httpclient.methods.GetMethod;
036: import org.apache.commons.httpclient.methods.HeadMethod;
037: import org.apache.commons.httpclient.methods.OptionsMethod;
038: import org.apache.commons.httpclient.methods.PostMethod;
039: import org.apache.commons.httpclient.methods.PutMethod;
040: import org.apache.commons.httpclient.methods.RequestEntity;
041: import org.apache.commons.httpclient.methods.TraceMethod;
042: import org.restlet.data.Method;
043: import org.restlet.data.Parameter;
044: import org.restlet.data.Protocol;
045: import org.restlet.data.Request;
046: import org.restlet.data.Status;
047: import org.restlet.resource.Representation;
048: import org.restlet.util.Series;
049:
050: import com.noelios.restlet.http.HttpClientCall;
051:
052: /**
053: * HTTP client connector call based on Apache HTTP Client's HttpMethod class.
054: *
055: * @author Jerome Louvel (contact@noelios.com)
056: */
057: public class HttpMethodCall extends HttpClientCall {
058: /** The associated HTTP client. */
059: private HttpClientHelper clientHelper;
060:
061: /** The wrapped HTTP method. */
062: private HttpMethod httpMethod;
063:
064: /** Indicates if the response headers were added. */
065: private boolean responseHeadersAdded;
066:
067: /**
068: * Constructor.
069: *
070: * @param helper
071: * The parent HTTP client helper.
072: * @param method
073: * The method name.
074: * @param requestUri
075: * The request URI.
076: * @param hasEntity
077: * Indicates if the call will have an entity to send to the
078: * server.
079: * @throws IOException
080: */
081: public HttpMethodCall(HttpClientHelper helper, final String method,
082: String requestUri, boolean hasEntity) throws IOException {
083: super (helper, method, requestUri);
084: this .clientHelper = helper;
085:
086: if (requestUri.startsWith("http")) {
087: if (method.equalsIgnoreCase(Method.GET.getName())) {
088: this .httpMethod = new GetMethod(requestUri);
089: } else if (method.equalsIgnoreCase(Method.POST.getName())) {
090: this .httpMethod = new PostMethod(requestUri);
091: } else if (method.equalsIgnoreCase(Method.PUT.getName())) {
092: this .httpMethod = new PutMethod(requestUri);
093: } else if (method.equalsIgnoreCase(Method.HEAD.getName())) {
094: this .httpMethod = new HeadMethod(requestUri);
095: } else if (method.equalsIgnoreCase(Method.DELETE.getName())) {
096: this .httpMethod = new DeleteMethod(requestUri);
097: } else if (method
098: .equalsIgnoreCase(Method.CONNECT.getName())) {
099: HostConfiguration host = new HostConfiguration();
100: host.setHost(new URI(requestUri, false));
101: this .httpMethod = new ConnectMethod(host);
102: } else if (method
103: .equalsIgnoreCase(Method.OPTIONS.getName())) {
104: this .httpMethod = new OptionsMethod(requestUri);
105: } else if (method.equalsIgnoreCase(Method.TRACE.getName())) {
106: this .httpMethod = new TraceMethod(requestUri);
107: } else {
108: this .httpMethod = new EntityEnclosingMethod(requestUri) {
109: public String getName() {
110: return method;
111: }
112: };
113: }
114:
115: this .httpMethod.setFollowRedirects(this .clientHelper
116: .isFollowRedirects());
117: this .httpMethod.setDoAuthentication(false);
118:
119: this .responseHeadersAdded = false;
120: setConfidential(this .httpMethod.getURI().getScheme()
121: .equalsIgnoreCase(Protocol.HTTPS.getSchemeName()));
122: } else {
123: throw new IllegalArgumentException(
124: "Only HTTP or HTTPS resource URIs are allowed here");
125: }
126: }
127:
128: /**
129: * Returns the HTTP method.
130: *
131: * @return The HTTP method.
132: */
133: public HttpMethod getHttpMethod() {
134: return this .httpMethod;
135: }
136:
137: /**
138: * Sends the request to the client. Commits the request line, headers and
139: * optional entity and send them over the network.
140: *
141: * @param request
142: * The high-level request.
143: * @return The result status.
144: */
145: @Override
146: public Status sendRequest(Request request) {
147: Status result = null;
148:
149: try {
150: final Representation entity = request.getEntity();
151:
152: // Set the request headers
153: for (Parameter header : getRequestHeaders()) {
154: getHttpMethod().setRequestHeader(header.getName(),
155: header.getValue());
156: }
157:
158: // For those method that accept enclosing entites, provide it
159: if ((entity != null)
160: && (getHttpMethod() instanceof EntityEnclosingMethod)) {
161: EntityEnclosingMethod eem = (EntityEnclosingMethod) getHttpMethod();
162: eem.setRequestEntity(new RequestEntity() {
163: public long getContentLength() {
164: return entity.getSize();
165: }
166:
167: public String getContentType() {
168: return (entity.getMediaType() != null) ? entity
169: .getMediaType().toString() : null;
170: }
171:
172: public boolean isRepeatable() {
173: return !entity.isTransient();
174: }
175:
176: public void writeRequest(OutputStream os)
177: throws IOException {
178: entity.write(os);
179: }
180: });
181: }
182:
183: // Ensure that the connection is active
184: this .clientHelper.getHttpClient().executeMethod(
185: getHttpMethod());
186:
187: // Now we can access the status code, this MUST happen after closing
188: // any open request stream.
189: result = new Status(getStatusCode(), null,
190: getReasonPhrase(), null);
191:
192: // If there is not response body, immediatly release the connection
193: if (getHttpMethod().getResponseBodyAsStream() == null) {
194: getHttpMethod().releaseConnection();
195: }
196: } catch (IOException ioe) {
197: this .clientHelper
198: .getLogger()
199: .log(
200: Level.WARNING,
201: "An error occurred during the communication with the remote HTTP server.",
202: ioe);
203: result = new Status(
204: Status.CONNECTOR_ERROR_COMMUNICATION,
205: "Unable to complete the HTTP call due to a communication error with the remote server. "
206: + ioe.getMessage());
207:
208: // Release the connection
209: getHttpMethod().releaseConnection();
210: }
211:
212: return result;
213: }
214:
215: /**
216: * Returns the response address.<br/> Corresponds to the IP address of the
217: * responding server.
218: *
219: * @return The response address.
220: */
221: public String getServerAddress() {
222: try {
223: return getHttpMethod().getURI().getHost();
224: } catch (URIException e) {
225: return null;
226: }
227: }
228:
229: /**
230: * Returns the modifiable list of response headers.
231: *
232: * @return The modifiable list of response headers.
233: */
234: public Series<Parameter> getResponseHeaders() {
235: Series<Parameter> result = super .getResponseHeaders();
236:
237: if (!this .responseHeadersAdded) {
238: for (Header header : getHttpMethod().getResponseHeaders()) {
239: result.add(header.getName(), header.getValue());
240: }
241:
242: this .responseHeadersAdded = true;
243: }
244:
245: return result;
246: }
247:
248: /**
249: * Returns the response status code.
250: *
251: * @return The response status code.
252: */
253: public int getStatusCode() {
254: return getHttpMethod().getStatusCode();
255: }
256:
257: /**
258: * Returns the response reason phrase.
259: *
260: * @return The response reason phrase.
261: */
262: public String getReasonPhrase() {
263: return getHttpMethod().getStatusText();
264: }
265:
266: /**
267: * Returns the response stream if it exists.
268: *
269: * @return The response stream if it exists.
270: */
271: public InputStream getResponseStream() {
272: InputStream result = null;
273:
274: try {
275: // Return a wrapper filter that will release the connection when
276: // needed
277: InputStream responseBodyAsStream = getHttpMethod()
278: .getResponseBodyAsStream();
279: if (responseBodyAsStream != null) {
280: result = new FilterInputStream(responseBodyAsStream) {
281: public void close() throws IOException {
282: super .close();
283: getHttpMethod().releaseConnection();
284: }
285: };
286: }
287: } catch (IOException ioe) {
288: result = null;
289: }
290:
291: return result;
292: }
293: }
|