/**
*
* @author greg (at) myrobotlab.org
*
* This file is part of MyRobotLab (http://myrobotlab.org).
*
* MyRobotLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version (subject to the "Classpath" exception
* as provided in the LICENSE.txt file that accompanied this code).
*
* MyRobotLab is distributed in the hope that it will be useful or fun,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* All libraries in thirdParty bundle are subject to their own license
* requirements - please refer to http://myrobotlab.org/libraries for
* details.
*
* Enjoy !
*
* */
package org.myrobotlab.service;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.myrobotlab.framework.Service;
import org.myrobotlab.framework.ServiceType;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.Logging;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.service.data.HttpData;
import org.myrobotlab.service.interfaces.HttpDataListener;
import org.myrobotlab.service.interfaces.HttpResponseListener;
import org.myrobotlab.service.interfaces.ServiceInterface;
import org.slf4j.Logger;
/**
* HttpClient - wrapper for Apache HttpClient
*
* @author GroG
*
* TODO - asynchronous call back similar to AngularJS promise - or at
* least a callback method is call .. onHttpResponse
*
* Synchronous or Asynchrounous - Synchronous by default, Asynchronous
* if a callback method is supplied or Non-Blocking method is called
*/
public class HttpClient extends Service implements HttpDataListener, HttpResponseListener {
private static final long serialVersionUID = 1L;
public final static Logger log = LoggerFactory.getLogger(HttpClient.class);
/**
* This static method returns all the details of the class without it having
* to be constructed. It has description, categories, dependencies, and peer
* definitions.
*
* @return ServiceType - returns all the data
*
*/
static public ServiceType getMetaData() {
ServiceType meta = new ServiceType(HttpClient.class.getCanonicalName());
meta.addDescription("an HTTP client, used to fetch information on the web");
meta.addCategory("network");
meta.addDependency("org.apache.commons.httpclient", "4.5.2");
return meta;
}
transient CloseableHttpClient client;
transient HashMap<String, String> formFields = new HashMap<String, String>();
public HttpClient(String n) {
super(n);
}
public void addFormField(String name, String value) {
formFields.put(name, value);
}
public void addHttpDataListener(ServiceInterface listener) {
/*
* TODO - finish this thought out .. it would mean a Map of method
* signatures to interface methods .. and direct callbacks Pro - is an
* optimization Con - is potentially blocking the callback thread for "too"
* long
*
* if (SerialDataListener.class.isAssignableFrom(listener.getClass()) &&
* listener.isLocal()) { // direct callback
* listeners.put(si.getName(),(SerialDataListener) si); } else {
*/
// pub sub
// instead of getting the data twice and expecting 2 methods for more or
// less the same material
// we will leave it up to the subscribing service to do subscribe and
// implement onHttpData
listener.subscribe(getName(), "publishHttpData");
// }
}
public void addHttpResponseListener(ServiceInterface listener) {
listener.subscribe(getName(), "publishHttpResponse");
}
public void clearForm() {
formFields.clear();
}
public String get(String uri) throws ClientProtocolException, IOException {
HttpData response = processResponse((HttpUriRequest) new HttpGet(uri), null);
if (response.data != null) {
return new String(response.data);
}
return null;
}
public byte[] getBytes(String uri) throws ClientProtocolException, IOException {
return processResponse((HttpUriRequest) new HttpGet(uri), null).data;
}
/**
* publishHttpData contains more information content type, response code,
* etc... need to subscribe to it manually for testing purposes
*
*/
@Override
public void onHttpData(HttpData data) {
log.info(data.toString());
}
/**
* for testing purposes
*
* @param data
* @return
*/
@Override
public void onHttpResponse(String data) {
log.info(data);
}
public String post(String uri) throws ClientProtocolException, IOException {
HttpData response = processResponse((HttpUriRequest) new HttpPost(uri), null);
if (response.data != null) {
return new String(response.data);
}
return null;
}
public String post(String uri, HashMap<String, String> fields) throws ClientProtocolException, IOException {
byte[] data = postBytes(uri, fields);
if (data != null) {
return new String(data);
}
return null;
}
public byte[] postBytes(String uri, HashMap<String, String> fields) throws ClientProtocolException, IOException {
return processResponse((HttpUriRequest) new HttpPost(uri), fields).data;
}
public HttpData processResponse(HttpUriRequest request, HashMap<String, String> fields) throws IOException {
HttpData data = new HttpData(request.getURI().toString());
if (fields == null) {
fields = formFields;
}
if (request.getClass().equals(HttpPost.class) && formFields.size() > 0) {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(fields.size());
for (String nvPairKey : fields.keySet()) {
nameValuePairs.add(new BasicNameValuePair(nvPairKey, fields.get(nvPairKey)));
((HttpPost) request).setEntity(new UrlEncodedFormEntity(nameValuePairs));
}
}
HttpResponse response = client.execute(request);
StatusLine statusLine = response.getStatusLine();
data.responseCode = statusLine.getStatusCode();
HttpEntity entity = response.getEntity();
Header header = entity.getContentType();
if (header != null) {
data.contentType = header.getValue().toString();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
response.getEntity().writeTo(baos);
data.data = baos.toByteArray();
// publishing
invoke("publishHttpData", data);
if (data.data != null) {
invoke("publishHttpResponse", new String(data.data));
}
return data;
}
/**
* publishing point for any http request this is the asynchronous callback
* which will arrive typically at publishHttpData(data)
*
* contains more data than just the text, can be used for any content type
* too, since the payload is in a byte[]
*
* @param data
* @return
*/
public HttpData publishHttpData(HttpData data) {
return data;
}
/**
* publishing point for any http request this is the asynchronous callback
* which will arrive typically at onHttpRespone(data)
*
* @param data
* @return
*/
public String publishHttpResponse(String data) {
return data;
}
public void startService() {
super.startService();
if (client == null) {
// new MultiThreadedHttpConnectionManager()
client = HttpClients.createDefault();
}
}
// Set the default host/protocol for the methods to connect to.
// This value will only be used if the methods are not given an absolute URI
// httpClient.getHostConfiguration().setHost("hc.apache.org", 80, "http");
// Map<String, HttpData> clients = new HashMap<String, HttpData>();
// TODO - proxy !
// TODO - authentication !
public static void main(String[] args) {
LoggingFactory.init(Level.INFO);
try {
HttpClient client = (HttpClient) Runtime.start("client", "HttpClient");
// this is how a listener might subscribe
// TODO - put dynamically subscribing into framework
// with interface inspection ??
client.addHttpResponseListener(client);
client.addHttpDataListener(client);
// TODO - getByteArray(...)
String index = client.get("https://www.cs.tut.fi/~jkorpela/forms/testing.html");
log.info(index);
client.addFormField("Comments", "This is a different comment");
client.addFormField("Box", "yes");
client.addFormField("Unexpected", "this is an unexpected field");
client.addFormField("hidden field", "something else");
String response = client.post("http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi");
log.info(response);
client.clearForm();
client.addFormField("hidden field", "something else");
response = client.post("http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi");
log.info(response);
response = client.get("http://www.google.com/search?hl=en&q=myrobotlab&btnG=Google+Search&aq=f&oq=");
log.info(response);
} catch (Exception e) {
Logging.logError(e);
}
}
}