/**
* 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.IOException;
import java.util.logging.Level;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.restlet.Client;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import com.noelios.restlet.http.HttpClientCall;
/**
* HTTP client connector using the HttpMethodCall and Apache HTTP Client
* project. Note that the response must be fully read in all cases in order to
* surely release the underlying connection. Not doing so may cause future
* requests to block.
*
* Here is the list of parameters that are supported:
* <table>
* <tr>
* <th>Parameter name</th>
* <th>Value type</th>
* <th>Default value</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>followRedirects</td>
* <td>boolean</td>
* <td>false</td>
* <td>If true, the protocol will automatically follow redirects. If false, the
* protocol will not automatically follow redirects.</td>
* </tr>
* <tr>
* <td>maxConnectionsPerHost</td>
* <td>int</td>
* <td>2 (uses HttpClient's default)</td>
* <td>The maximum number of connections that will be created for any particular
* host.</td>
* </tr>
* <tr>
* <td>maxTotalConnections</td>
* <td>int</td>
* <td>20 (uses HttpClient's default)</td>
* <td>The maximum number of active connections.</td>
* </tr>
* <tr>
* <td>connectionManagerTimeout</td>
* <td>int</td>
* <td>0</td>
* <td>The timeout in milliseconds used when retrieving an HTTP connection from
* the HTTP connection manager.</td>
* </tr>
* <tr>
* <td>stopIdleTimeout</td>
* <td>int</td>
* <td>1000</td>
* <td>The minimum idle time, in milliseconds, for connections to be closed when
* stopping the connector.</td>
* </tr>
* <tr>
* <td>readTimeout</td>
* <td>int</td>
* <td>0</td>
* <td>Sets the read timeout to a specified timeout, in milliseconds. A timeout
* of zero is interpreted as an infinite timeout.</td>
* </tr>
* <tr>
* <td>retryHandler</td>
* <td>String</td>
* <td>null</td>
* <td>Class name of the retry handler to use instead of HTTP Client default
* behavior. The given class name must implement the
* org.apache.commons.httpclient.HttpMethodRetryHandler interface and have a
* default constructor</td>
* </tr>
* <tr>
* <td>tcpNoDelay</td>
* <td>boolean</td>
* <td>false</td>
* <td>Indicate if Nagle's TCP_NODELAY algorithm should be used.</td>
* </tr>
* </table>
*
* @see <a href=
* "http://jakarta.apache.org/httpcomponents/httpclient-3.x/tutorial.html"
* >Apache HTTP Client tutorial</a>
* @see <a
* href="http://java.sun.com/j2se/1.5.0/docs/guide/net/index.html">Networking
* Features</a>
* @author Jerome Louvel
*/
public class HttpClientHelper extends com.noelios.restlet.http.HttpClientHelper {
private volatile HttpClient httpClient;
/**
* Constructor.
*
* @param client
* The client to help.
*/
public HttpClientHelper(Client client) {
super(client);
this.httpClient = null;
getProtocols().add(Protocol.HTTP);
getProtocols().add(Protocol.HTTPS);
}
/**
* Creates a low-level HTTP client call from a high-level uniform call.
*
* @param request
* The high-level request.
* @return A low-level HTTP client call.
*/
@Override
public HttpClientCall create(Request request) {
HttpClientCall result = null;
try {
result = new HttpMethodCall(this, request.getMethod().toString(),
request.getResourceRef().toString(), request
.isEntityAvailable());
} catch (IOException ioe) {
getLogger().log(Level.WARNING,
"Unable to create the HTTP client call", ioe);
}
return result;
}
/**
* Returns the timeout in milliseconds used when retrieving an HTTP
* connection from the HTTP connection manager.
*
* @return The timeout in milliseconds used when retrieving an HTTP
* connection from the HTTP connection manager.
*/
public int getConnectionManagerTimeout() {
return Integer.parseInt(getHelpedParameters().getFirstValue(
"connectionManagerTimeout", "0"));
}
public HttpClient getHttpClient() {
return this.httpClient;
}
/**
* Returns the maximum number of connections that will be created for any
* particular host.
*
* @return The maximum number of connections that will be created for any
* particular host.
*/
public int getMaxConnectionsPerHost() {
return Integer.parseInt(getHelpedParameters().getFirstValue(
"maxConnectionsPerHost", "2"));
}
/**
* Returns the maximum number of active connections.
*
* @return The maximum number of active connections.
*/
public int getMaxTotalConnections() {
return Integer.parseInt(getHelpedParameters().getFirstValue(
"maxTotalConnections", "20"));
}
/**
* Returns the read timeout value. A timeout of zero is interpreted as an
* infinite timeout.
*
* @return The read timeout value.
*/
public int getReadTimeout() {
return Integer.parseInt(getHelpedParameters().getFirstValue(
"readTimeout", "0"));
}
/**
* Returns the class name of the retry handler to use instead of HTTP Client
* default behavior. The given class name must implement the
* org.apache.commons.httpclient.HttpMethodRetryHandler interface and have a
* default constructor.
*
* @return The class name of the retry handler.
*/
public String getRetryHandler() {
return getHelpedParameters().getFirstValue("retryHandler", null);
}
/**
* Returns the minimum idle time, in milliseconds, for connections to be
* closed when stopping the connector.
*
* @return The minimum idle time, in milliseconds, for connections to be
* closed when stopping the connector.
*/
public int getStopIdleTimeout() {
return Integer.parseInt(getHelpedParameters().getFirstValue(
"stopIdleTimeout", "1000"));
}
/**
* Indicates if the protocol will automatically follow redirects.
*
* @return True if the protocol will automatically follow redirects.
*/
public boolean isFollowRedirects() {
return Boolean.parseBoolean(getHelpedParameters().getFirstValue(
"followRedirects", "false"));
}
/**
* Indicates if the protocol will use Nagle's algorithm
*
* @return True to enable TCP_NODELAY, false to disable.
* @see java.net.Socket#setTcpNoDelay(boolean)
*/
public boolean getTcpNoDelay() {
return Boolean.parseBoolean(getHelpedParameters().getFirstValue(
"tcpNoDelay", "false"));
}
@Override
public void start() throws Exception {
super.start();
// Create the multi-threaded connection manager and configure it
final MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
connectionManager.getParams().setDefaultMaxConnectionsPerHost(
getMaxConnectionsPerHost());
connectionManager.getParams().setMaxTotalConnections(
getMaxTotalConnections());
connectionManager.getParams().setTcpNoDelay(getTcpNoDelay());
// Create the internal client connector
this.httpClient = new HttpClient(connectionManager);
getHttpClient().getParams().setAuthenticationPreemptive(false);
getHttpClient().getParams().setConnectionManagerTimeout(
getConnectionManagerTimeout());
getHttpClient().getParams()
.setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
getHttpClient().getParams().setSoTimeout(getReadTimeout());
getLogger().info("Starting the HTTP client");
}
@Override
public void stop() throws Exception {
getHttpClient().getHttpConnectionManager().closeIdleConnections(
getStopIdleTimeout());
getLogger().info("Stopping the HTTP client");
}
}