// Copyright 2012 Georg-August-Universität Göttingen, Germany // // 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. package de.ugoe.cs.autoquest.httpmonitor.proxy; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response.CompleteListener; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.OutputStreamContentProvider; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.thread.QueuedThreadPool; import de.ugoe.cs.autoquest.httpmonitor.HttpMonitor; import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorComponent; import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorException; import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorExchangeHandler; import de.ugoe.cs.autoquest.plugin.http.logdata.HttpExchange; import de.ugoe.cs.autoquest.plugin.http.logdata.ObjectFactory; import de.ugoe.cs.util.console.Console; /** *

* If the exchanges recorded by the proxy are to be transmitted to a central {@link HttpMonitor}, * this exchanges handler is used. It is called by the exchange listener on completion of a proxied * request/response. It then creates an HTTP request to the central monitor and sends it there. * It is initialized with the name of the server and the port on which the central monitor runs. * If the exchanges can not be forwarded to the central server, they are discarded. *

* * @author Patrick Harms */ public class HttpMonitorRemoteExchangeHandler implements CompleteListener, HttpMonitorExchangeHandler, HttpMonitorComponent { /** *

* the HTTP client used internally to send data to the central server *

*/ private HttpClient httpClient; /** *

* the host name of the central server *

*/ private String httpMonitorServer; /** *

* the port of the central server *

*/ private int httpMonitorPort; /** *

* a set of requests send to the central server for which the response was not received yet *

*/ private Set openRequests = new HashSet(); /** *

* initializes the exchange handler with the host and port of the central server *

* * @param httpMonitorServer the host name of the central server * @param httpMonitorPort the port of the central server */ public HttpMonitorRemoteExchangeHandler(String httpMonitorServer, int httpMonitorPort) { this.httpMonitorServer = httpMonitorServer; this.httpMonitorPort = httpMonitorPort; } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.httpmonitor.HttpMonitorComponent#init() */ @Override public void init() throws IllegalStateException, HttpMonitorException { httpClient = createHttpClient(); } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.httpmonitor.HttpMonitorComponent#start() */ @Override public void start() throws IllegalStateException, HttpMonitorException { try { httpClient.start(); httpClient.getContentDecoderFactories().clear(); } catch (Exception e) { throw new HttpMonitorException("could not start client for HTTP-Monitor", e); } } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.httpmonitor.HttpMonitorComponent#stop() */ @Override public void stop() { if (httpClient != null) { try { httpClient.stop(); } catch (Exception e) { Console.traceln(Level.WARNING, "could not stop client for HTTP-Monitor"); Console.logException(e); } } httpClient = null; } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.httpmonitor.HttpMonitorExchangeHandler#handleHttpExchange() */ @Override public void handleHttpExchange(HttpExchange httpExchange) { // send the exchange to the server and wait for completion synchronized (openRequests) { Request httpMonitorRequest = httpClient.newRequest(httpMonitorServer, httpMonitorPort); httpMonitorRequest.method(HttpMethod.POST); httpMonitorRequest.version(HttpVersion.HTTP_1_1); OutputStreamContentProvider out = new OutputStreamContentProvider(); httpMonitorRequest.content(out); httpMonitorRequest.send(this); try { JAXBContext jaxbContext = JAXBContext.newInstance(HttpExchange.class.getPackage().getName()); Marshaller marshaller = jaxbContext.createMarshaller(); OutputStreamWriter writer = new OutputStreamWriter(out.getOutputStream(), "UTF-8"); marshaller.marshal(new ObjectFactory().createHttpExchange(httpExchange), writer); out.getOutputStream().close(); } catch (JAXBException e) { Console.printerrln("could not convert request and response to XML string: " + e); Console.logException(e); } catch (IOException e) { Console.printerrln ("could not close the stream for sending data to the HTML monitor: " + e); Console.logException(e); } openRequests.add(httpMonitorRequest); try { // wait for the request to be removed fromt the list asynchronously while (openRequests.contains(httpMonitorRequest)) { openRequests.wait(); } } catch (InterruptedException e) { // ignore, as this may only happen on system shutdown } } } /* (non-Javadoc) * @see org.eclipse.jetty.client.api.Response.CompleteListener#onComplete(Result) */ @Override public void onComplete(Result result) { if (result.isFailed()) { Console.traceln (Level.WARNING, "could not log exchange correctly: " + result.getFailure()); Console.logException((Exception) result.getFailure()); } synchronized (openRequests) { openRequests.remove(result.getRequest()); openRequests.notify(); } } /** *

* convenience method to create an initialize the utilized HTTP client *

*/ private HttpClient createHttpClient() { HttpClient client = new HttpClient(); QueuedThreadPool executor = new QueuedThreadPool(10); executor.setName("HttpMonitorClients"); client.setExecutor(executor); client.setMaxConnectionsPerDestination(10000); client.setIdleTimeout(30000); client.setConnectTimeout(30000); client.setStopTimeout(30000); client.setRequestBufferSize(1024*1024); client.setResponseBufferSize(1024*1024); return client; } }