package com.netifera.platform.net.http.service; import java.io.IOException; import java.io.InterruptedIOException; import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicInteger; import org.apache.http.HttpException; import org.apache.http.ProtocolException; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.nio.DefaultClientIOEventDispatch; import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; import org.apache.http.nio.NHttpConnection; import org.apache.http.nio.protocol.BufferingHttpClientHandler; import org.apache.http.nio.protocol.EventListener; import org.apache.http.nio.protocol.HttpRequestExecutionHandler; import org.apache.http.nio.reactor.ConnectingIOReactor; import org.apache.http.nio.reactor.IOEventDispatch; import org.apache.http.nio.reactor.IOReactorException; import org.apache.http.nio.reactor.SessionRequest; import org.apache.http.nio.reactor.SessionRequestCallback; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.RequestConnControl; import org.apache.http.protocol.RequestContent; import org.apache.http.protocol.RequestExpectContinue; import org.apache.http.protocol.RequestUserAgent; import com.netifera.platform.api.log.ILogger; import com.netifera.platform.net.http.internal.service.Activator; import com.netifera.platform.util.locators.TCPSocketLocator; public class AsynchronousHTTPClient { private final TCPSocketLocator locator; private final ILogger logger; private final ConnectingIOReactor ioReactor; private final EventListener connectionsListener; private final AtomicInteger connectionsCount = new AtomicInteger(0); private class ConnectionsListener implements EventListener { public void connectionOpen(final NHttpConnection conn) { // connectionsCount.incrementAndGet(); //!!already in the SessionRequestCallback logger.debug(info("Connection open")); if (connectionsListener != null) connectionsListener.connectionOpen(conn); } public void connectionTimeout(final NHttpConnection conn) { // connectionsCount.decrementAndGet(); logger.debug(info("Connection timed out")); if (connectionsListener != null) connectionsListener.connectionTimeout(conn); } public void connectionClosed(final NHttpConnection conn) { connectionsCount.decrementAndGet(); logger.debug(info("Connection closed")); if (connectionsListener != null) connectionsListener.connectionClosed(conn); } public void fatalIOException(final IOException ex, final NHttpConnection conn) { // connectionsCount.decrementAndGet(); //XXX is this ok? logger.error(info("I/O error"), ex); if (connectionsListener != null) connectionsListener.fatalIOException(ex, conn); } public void fatalProtocolException(final HttpException ex, final NHttpConnection conn) { // connectionsCount.decrementAndGet(); //XXX is this ok? if (ex instanceof ProtocolException) { logger.error(info("Protocol error") + ": " + ex.getMessage()); } else { logger.debug(debug("HTTP error", conn), ex); } if (connectionsListener != null) connectionsListener.fatalProtocolException(ex, conn); } private String info(String errtype) { return errtype + " at " + locator; } private String debug(String errtype, NHttpConnection conn) { return info(errtype) + ", count:" + connectionsCount + ", " + conn; } } public AsynchronousHTTPClient(TCPSocketLocator locator, EventListener connectionsListener, HttpRequestExecutionHandler requestHandler) throws IOReactorException { // TODO better logging with loggingContext this(locator, connectionsListener, requestHandler, Activator.getInstance().getLogManager().getLogger("Asynchronous HTTP Client")); } public AsynchronousHTTPClient(TCPSocketLocator locator, EventListener connectionsListener, HttpRequestExecutionHandler requestHandler, final ILogger logger) throws IOReactorException { this.locator = locator; this.connectionsListener = connectionsListener; this.logger = logger; HttpParams params = new BasicHttpParams(); params .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000) .setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000) .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true) .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) .setParameter(CoreProtocolPNames.USER_AGENT, HTTPClient.DEFAULT_USER_AGENT); ioReactor = new DefaultConnectingIOReactor(2, params); BasicHttpProcessor httpproc = new BasicHttpProcessor(); httpproc.addInterceptor(new RequestContent()); // XXX the user should set target host // httpproc.addInterceptor(new RequestTargetHost()); httpproc.addInterceptor(new RequestConnControl()); httpproc.addInterceptor(new RequestUserAgent()); httpproc.addInterceptor(new RequestExpectContinue()); BufferingHttpClientHandler handler = new BufferingHttpClientHandler( httpproc, requestHandler, new DefaultConnectionReuseStrategy(), params); handler.setEventListener(new ConnectionsListener()); final IOEventDispatch ioEventDispatch = new DefaultClientIOEventDispatch( handler, params); Thread t = new Thread(new Runnable() { public void run() { try { ioReactor.execute(ioEventDispatch); } catch (InterruptedIOException ex) { //logger.warning("Interrupted"); } catch (IOException e) { logger.error("I/O error", e); } logger.debug("Shutdown HTTP Client"); } }); t.setName("HTTP Client on " + locator); t.start(); } @Deprecated // XXX could never return on error: infinite loop -> need detailed javadoc public void connect() { connect(null); } public synchronized void connect(final SessionRequestCallback callback) { connectionsCount.incrementAndGet(); ioReactor.connect(new InetSocketAddress(locator.getAddress() .toInetAddress(), locator.getPort()), null, null, new SessionRequestCallback() { public void cancelled(SessionRequest request) { connectionsCount.decrementAndGet(); if (callback != null) callback.cancelled(request); } public void completed(SessionRequest request) { if (callback != null) callback.completed(request); } public void failed(SessionRequest request) { connectionsCount.decrementAndGet(); if (callback != null) callback.failed(request); } public void timeout(SessionRequest request) { connectionsCount.decrementAndGet(); if (callback != null) callback.timeout(request); } }); } public void shutdown() throws IOException { ioReactor.shutdown(); } public int getConnectionsCount() { return connectionsCount.get(); } }