com.noelios.restlet.ext.httpclient.HttpMethodCall.java Source code

Java tutorial

Introduction

Here is the source code for com.noelios.restlet.ext.httpclient.HttpMethodCall.java

Source

/**
 * Copyright 2005-2008 Noelios Technologies.
 * 
 * The contents of this file are subject to the terms of the following open
 * source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 (the "Licenses"). You can
 * select the license that you prefer but you may not use this file except in
 * compliance with one of these Licenses.
 * 
 * You can obtain a copy of the LGPL 3.0 license at
 * http://www.gnu.org/licenses/lgpl-3.0.html
 * 
 * You can obtain a copy of the LGPL 2.1 license at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 * 
 * You can obtain a copy of the CDDL 1.0 license at
 * http://www.sun.com/cddl/cddl.html
 * 
 * See the Licenses for the specific language governing permissions and
 * limitations under the Licenses.
 * 
 * Alternatively, you can obtain a royaltee free commercial license with less
 * limitations, transferable or non-transferable, directly at
 * http://www.noelios.com/products/restlet-engine
 * 
 * Restlet is a registered trademark of Noelios Technologies.
 */

package com.noelios.restlet.ext.httpclient;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.logging.Level;

import org.apache.commons.httpclient.ConnectMethod;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.OptionsMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.TraceMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.restlet.data.Method;
import org.restlet.data.Parameter;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.data.Status;
import org.restlet.resource.Representation;
import org.restlet.util.Engine;
import org.restlet.util.Series;

import com.noelios.restlet.http.HttpClientCall;

/**
 * HTTP client connector call based on Apache HTTP Client's HttpMethod class.
 * 
 * @author Jerome Louvel
 */
public class HttpMethodCall extends HttpClientCall {

    /** The associated HTTP client. */
    private volatile HttpClientHelper clientHelper;

    /** The wrapped HTTP method. */
    private volatile HttpMethod httpMethod;

    /** Indicates if the response headers were added. */
    private volatile boolean responseHeadersAdded;

    /**
     * Constructor.
     * 
     * @param helper
     *            The parent HTTP client helper.
     * @param method
     *            The method name.
     * @param requestUri
     *            The request URI.
     * @param hasEntity
     *            Indicates if the call will have an entity to send to the
     *            server.
     * @throws IOException
     */
    public HttpMethodCall(HttpClientHelper helper, final String method, String requestUri, boolean hasEntity)
            throws IOException {
        super(helper, method, requestUri);
        this.clientHelper = helper;

        if (requestUri.startsWith("http")) {
            if (method.equalsIgnoreCase(Method.GET.getName())) {
                this.httpMethod = new GetMethod(requestUri);
            } else if (method.equalsIgnoreCase(Method.POST.getName())) {
                this.httpMethod = new PostMethod(requestUri);
            } else if (method.equalsIgnoreCase(Method.PUT.getName())) {
                this.httpMethod = new PutMethod(requestUri);
            } else if (method.equalsIgnoreCase(Method.HEAD.getName())) {
                this.httpMethod = new HeadMethod(requestUri);
            } else if (method.equalsIgnoreCase(Method.DELETE.getName())) {
                this.httpMethod = new DeleteMethod(requestUri);
            } else if (method.equalsIgnoreCase(Method.CONNECT.getName())) {
                final HostConfiguration host = new HostConfiguration();
                host.setHost(new URI(requestUri, false));
                this.httpMethod = new ConnectMethod(host);
            } else if (method.equalsIgnoreCase(Method.OPTIONS.getName())) {
                this.httpMethod = new OptionsMethod(requestUri);
            } else if (method.equalsIgnoreCase(Method.TRACE.getName())) {
                this.httpMethod = new TraceMethod(requestUri);
            } else {
                this.httpMethod = new EntityEnclosingMethod(requestUri) {
                    @Override
                    public String getName() {
                        return method;
                    }
                };
            }

            this.httpMethod.setFollowRedirects(this.clientHelper.isFollowRedirects());
            this.httpMethod.setDoAuthentication(false);

            if (this.clientHelper.getRetryHandler() != null) {
                try {
                    this.httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                            Engine.loadClass(this.clientHelper.getRetryHandler()).newInstance());
                } catch (Exception e) {
                    this.clientHelper.getLogger().log(Level.WARNING,
                            "An error occurred during the instantiation of the retry handler.", e);
                }
            }

            this.responseHeadersAdded = false;
            setConfidential(this.httpMethod.getURI().getScheme().equalsIgnoreCase(Protocol.HTTPS.getSchemeName()));
        } else {
            throw new IllegalArgumentException("Only HTTP or HTTPS resource URIs are allowed here");
        }
    }

    /**
     * Returns the HTTP method.
     * 
     * @return The HTTP method.
     */
    public HttpMethod getHttpMethod() {
        return this.httpMethod;
    }

    /**
     * Returns the response reason phrase.
     * 
     * @return The response reason phrase.
     */
    @Override
    public String getReasonPhrase() {
        return getHttpMethod().getStatusText();
    }

    @Override
    public WritableByteChannel getRequestEntityChannel() {
        return null;
    }

    @Override
    public OutputStream getRequestEntityStream() {
        return null;
    }

    @Override
    public OutputStream getRequestHeadStream() {
        return null;
    }

    @Override
    public ReadableByteChannel getResponseEntityChannel(long size) {
        return null;
    }

    @Override
    public InputStream getResponseEntityStream(long size) {
        InputStream result = null;

        try {
            // Return a wrapper filter that will release the connection when
            // needed
            final InputStream responseBodyAsStream = getHttpMethod().getResponseBodyAsStream();
            if (responseBodyAsStream != null) {
                result = new FilterInputStream(responseBodyAsStream) {
                    @Override
                    public void close() throws IOException {
                        super.close();
                        getHttpMethod().releaseConnection();
                    }
                };
            }
        } catch (IOException ioe) {
        }

        return result;
    }

    /**
     * Returns the modifiable list of response headers.
     * 
     * @return The modifiable list of response headers.
     */
    @Override
    public Series<Parameter> getResponseHeaders() {
        final Series<Parameter> result = super.getResponseHeaders();

        if (!this.responseHeadersAdded) {
            for (final Header header : getHttpMethod().getResponseHeaders()) {
                result.add(header.getName(), header.getValue());
            }

            this.responseHeadersAdded = true;
        }

        return result;
    }

    /**
     * Returns the response address.<br>
     * Corresponds to the IP address of the responding server.
     * 
     * @return The response address.
     */
    @Override
    public String getServerAddress() {
        try {
            return getHttpMethod().getURI().getHost();
        } catch (URIException e) {
            return null;
        }
    }

    /**
     * Returns the response status code.
     * 
     * @return The response status code.
     */
    @Override
    public int getStatusCode() {
        return getHttpMethod().getStatusCode();
    }

    /**
     * Sends the request to the client. Commits the request line, headers and
     * optional entity and send them over the network.
     * 
     * @param request
     *            The high-level request.
     * @return The result status.
     */
    @Override
    public Status sendRequest(Request request) {
        Status result = null;

        try {
            final Representation entity = request.getEntity();

            // Set the request headers
            for (final Parameter header : getRequestHeaders()) {
                getHttpMethod().addRequestHeader(header.getName(), header.getValue());
            }

            // For those method that accept enclosing entities, provide it
            if ((entity != null) && (getHttpMethod() instanceof EntityEnclosingMethod)) {
                final EntityEnclosingMethod eem = (EntityEnclosingMethod) getHttpMethod();
                eem.setRequestEntity(new RequestEntity() {
                    public long getContentLength() {
                        return entity.getSize();
                    }

                    public String getContentType() {
                        return (entity.getMediaType() != null) ? entity.getMediaType().toString() : null;
                    }

                    public boolean isRepeatable() {
                        return !entity.isTransient();
                    }

                    public void writeRequest(OutputStream os) throws IOException {
                        entity.write(os);
                    }
                });
            }

            // Ensure that the connection is active
            this.clientHelper.getHttpClient().executeMethod(getHttpMethod());

            // Now we can access the status code, this MUST happen after closing
            // any open request stream.
            result = new Status(getStatusCode(), null, getReasonPhrase(), null);

            // If there is no response body, immediately release the connection
            if (getHttpMethod().getResponseBodyAsStream() == null) {
                getHttpMethod().releaseConnection();
            }
        } catch (IOException ioe) {
            this.clientHelper.getLogger().log(Level.WARNING,
                    "An error occurred during the communication with the remote HTTP server.", ioe);
            result = new Status(Status.CONNECTOR_ERROR_COMMUNICATION, ioe);

            // Release the connection
            getHttpMethod().releaseConnection();
        }

        return result;
    }
}