From: <do...@us...> - 2017-04-05 08:25:44
|
Revision: 20205 http://sourceforge.net/p/gate/code/20205 Author: domrout Date: 2017-04-05 08:25:42 +0000 (Wed, 05 Apr 2017) Log Message: ----------- Actually commit the new version of WebUtils Added Paths: ----------- mimir/trunk/mimir-client/src/gate/mimir/tool/WebUtils.java Added: mimir/trunk/mimir-client/src/gate/mimir/tool/WebUtils.java =================================================================== --- mimir/trunk/mimir-client/src/gate/mimir/tool/WebUtils.java (rev 0) +++ mimir/trunk/mimir-client/src/gate/mimir/tool/WebUtils.java 2017-04-05 08:25:42 UTC (rev 20205) @@ -0,0 +1,507 @@ +/* + * WebUtils.java + * + * Copyright (c) 2007-2011, The University of Sheffield. + * + * This file is part of GATE Mímir (see http://gate.ac.uk/family/mimir.html), + * and is free software, licenced under the GNU Lesser General Public License, + * Version 3, June 2007 (also included with this distribution as file + * LICENCE-LGPL3.html). + * + * Dominic Rout 5 Apr 2017 + * Valentin Tablan, 29 Jan 2010 + * + * $Id: WebUtils.java 17423 2014-02-26 10:36:54Z valyt $ + */ +package gate.mimir.tool; + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CookieStore; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +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.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.SerializableEntity; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.protocol.HttpContext; +import sun.net.www.protocol.http.HttpURLConnection; + +import java.io.*; +import java.net.URISyntaxException; +import java.nio.CharBuffer; +import java.util.concurrent.TimeUnit; + +/** + * A collection of methods that provide various utility functions for web + * applications. + */ +public class WebUtils { + + protected PoolingHttpClientConnectionManager connectionManager; + protected CredentialsProvider credsProvider; + protected CloseableHttpClient client; + protected boolean hasContext; + protected CookieStore cookieJar; + protected UsernamePasswordCredentials creds; + + + public WebUtils() { + this(null, null, null, 10); + } + + public WebUtils(CookieStore cookieJar) { + this(null, null, null, 10); + } + + public WebUtils(String userName, String password) { + this(null, userName, password, 10); + } + + public WebUtils(CookieStore cookieJar, + String userName, String password) { + this(cookieJar, userName, password, 10); + + } + + public WebUtils(CookieStore cookieJar, + String userName, String password, int maxConnections) { + connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS); + // Increase max total connection to 200 + connectionManager.setMaxTotal(maxConnections); + // Increase default max connection per route to 20 + connectionManager.setDefaultMaxPerRoute(maxConnections); + + this.cookieJar = cookieJar; + + + hasContext = cookieJar != null || userName != null || password != null; + + credsProvider = new BasicCredentialsProvider(); + if (userName != null && password != null) { + creds = new UsernamePasswordCredentials(userName, password); + } else { + creds = null; + } + + RequestConfig globalConfig = RequestConfig.custom() + .setCookieSpec(CookieSpecs.DEFAULT) + .build(); + + client = HttpClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(globalConfig) + .setDefaultCookieStore(cookieJar) + .build(); + } + + /** + * Constructs a URL from a base URL segment and a set of query parameters. + * @param urlBase the string that will be the prefix of the returned. + * This should include everything apart from the query part of the URL. + * @param params an array of String values, which should contain alternating + * parameter names and parameter values. It is obvious that the size of this + * array must be an even number. + * @return a URl built according to the provided parameters. If for example + * the following parameter values are provided: <b>urlBase:</b> + * <tt>http://host:8080/appName/service</tt>; <b>params:</b> <tt>foo1, bar1, + * foo2, bar2, foo3, bar3</tt>, then the following URL would be returned: + * <tt>http://host:8080/appName/service?foo1=bar1&foo2=bar2&foo3=bar3</tt> + */ + public static String buildUrl(String urlBase, String... params){ + StringBuilder str = new StringBuilder(urlBase); + if(params != null && params.length > 0){ + str.append('?'); + for(int i = 0 ; i < (params.length/2) - 1; i++){ + str.append(params[i * 2]); + str.append('='); + str.append(params[i * 2 + 1]); + str.append('&'); + } + //and now, the last parameter + str.append(params[params.length - 2]); + str.append('='); + str.append(params[params.length - 1]); + } + return str.toString(); + } + + + protected HttpContext getContext() { + HttpClientContext context = HttpClientContext.create(); + context.setCredentialsProvider(credsProvider); + if (this.cookieJar != null) { + context.setCookieStore(this.cookieJar); + } + + return context; + } + + public CloseableHttpResponse execute(HttpUriRequest request) throws IOException { + // If we have a context, we have to generate a new one for each request, + // because sharing them between threads seems to break after a few hundred thousan + // requests. + if (hasContext) { + // Fetch a context to use. + HttpContext context = getContext(); + + if (creds != null) { + // Attach any credentials provided to the given host. + credsProvider.setCredentials( + new AuthScope(request.getURI().getHost(), + AuthScope.ANY_PORT), + creds); + } + + // Run the request. + return this.client.execute(request, context); + } else { + // No cookies or auth needed - just run the request as is. + return this.client.execute(request); + } + } + + /** + * Calls a web service action (i.e. it connects to a URL). If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will write all content available from the + * input stream of the resulting connection to the provided Appendable. + * + * @param out an {@link Appendable} to which the output is written. + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public void getText(final Appendable out, String baseUrl, String... params) + throws IOException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + new RequestExecutor<Void>(this) + .runRequest(request, response -> { + InputStream contentInputStream = response.getEntity().getContent(); + try { + Reader r = new InputStreamReader(contentInputStream, "UTF-8"); + char[] bufArray = new char[4096]; + CharBuffer buf = CharBuffer.wrap(bufArray); + int charsRead = -1; + while ((charsRead = r.read(bufArray)) >= 0) { + buf.position(0); + buf.limit(charsRead); + out.append(buf); + + } + } finally { + contentInputStream.close(); + } + return null; + }); + } + + /** + * Calls a web service action (i.e. it connects to a URL), and reads a + * serialised int value from the resulting connection. If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all additional content available from + * either the input and error streams of the resulting connection (which + * should permit connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public int getInt(String baseUrl, String... params) + throws IOException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + return new RequestExecutor<Integer>(this) + .runObjectRequest(request, (ObjectInputStream o) -> o.readInt()); + } + + /** + * Calls a web service action (i.e. it connects to a URL). If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all content available from either the + * input and error streams of the resulting connection (which should permit + * connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public void getVoid(String baseUrl, String... params) throws IOException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + new RequestExecutor<Void>(this) + .runRequest(request, response -> null); + } + + /** + * Calls a web service action (i.e. it connects to a URL), and reads a + * serialised long value from the resulting connection. If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all additional content available from + * either the input and error streams of the resulting connection (which + * should permit connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public long getLong(String baseUrl, String... params) + throws IOException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + return new RequestExecutor<Long>(this) + .runObjectRequest(request, (ObjectInputStream o) -> o.readLong()); + } + + /** + * Calls a web service action (i.e. it connects to a URL), and reads a + * serialised double value from the resulting connection. If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all additional content available from + * either the input and error streams of the resulting connection (which + * should permit connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public double getDouble(String baseUrl, String... params) + throws IOException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + return new RequestExecutor<Double>(this) + .runObjectRequest(request, (ObjectInputStream o) -> o.readDouble()); + } + + /** + * Calls a web service action (i.e. it connects to a URL), and reads a + * serialised boolean value from the resulting connection. If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all additional content available from + * either the input and error streams of the resulting connection (which + * should permit connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public boolean getBoolean(String baseUrl, String... params) + throws IOException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + return new RequestExecutor<Boolean>(this) + .runObjectRequest(request, ObjectInputStream::readBoolean); + } + + /** + * Calls a web service action (i.e. it connects to a URL), and reads a + * serialised Object value from the resulting connection. If the connection + * fails, for whatever reason, or the response code is different from + * {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all additional content available from + * either the input and error streams of the resulting connection (which + * should permit connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + * @throws ClassNotFoundException if the value read from the remote connection + * is of a type unknown to the local JVM. + */ + public Object getObject(String baseUrl, String... params) + throws IOException, ClassNotFoundException { + HttpGet request = new HttpGet(buildUrl(baseUrl, params)); + + try { + return new RequestExecutor<>(this) + .runObjectRequest(request, ObjectInputStream::readObject); + + } catch (RuntimeException e) { + if (e.getCause() instanceof ClassNotFoundException) { + throw (ClassNotFoundException) e.getCause(); + } else { + throw e; + } + } + } + + /** + * Calls a web service action (i.e. it connects to a URL) using the POST HTTP + * method, sending the given object in Java serialized format as the request + * body. The request is sent using chunked transfer encoding, and the + * request's Content-Type is set to application/octet-stream. If the + * connection fails, for whatever reason, or the response code is different + * from {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all content available from either the + * input and error streams of the resulting connection (which should permit + * connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param object the object to serialize and send in the POST body + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public void postObject(String baseUrl, Serializable object, + String... params) throws IOException { + HttpPost request = new HttpPost(buildUrl(baseUrl, params)); + + request.setHeader("Content-Type", "application/octet-stream"); + // Set up the entity to send to the server. + SerializableEntity entity = new SerializableEntity(object); + entity.setChunked(true); + request.setEntity(entity); + + // Now run the request + new RequestExecutor<Void>(this) + .runRequest(request, a -> null); + } + + /** + * Calls a web service action (i.e. it connects to a URL) using the POST HTTP + * method, sending the given bytes as the request + * body. The request is sent using chunked transfer encoding, and the + * request's Content-Type is set to application/octet-stream. If the + * connection fails, for whatever reason, or the response code is different + * from {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * This method will drain (and discard) all content available from either the + * input and error streams of the resulting connection (which should permit + * connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param data a {@link ByteArrayOutputStream} containing the data to be + * written. Its {@link ByteArrayOutputStream#writeTo(OutputStream)} method + * will be called causing it to write its data to the output connection. + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @throws IOException if the connection fails. + */ + public void postData(String baseUrl, ByteArrayOutputStream data, + String... params) throws IOException { + HttpPost request = new HttpPost(buildUrl(baseUrl, params)); + + request.setHeader("Content-Type", "application/octet-stream"); + + ByteArrayEntity entity = new ByteArrayEntity(data.toByteArray()); + entity.setChunked(true); + request.setEntity(entity); + + new RequestExecutor<Void>(this) + .runRequest(request, a -> null); + } + + /** + * Calls a web service action (i.e. it connects to a URL) using the POST HTTP + * method, sending the given object in Java serialized format as the request + * body. The request is sent using chunked transfer encoding, and the + * request's Content-Type is set to application/octet-stream. If the + * connection fails, for whatever reason, or the response code is different + * from {@link HttpURLConnection#HTTP_OK}, then an IOException is raised. + * The response from the server is read and Java-deserialized, the resulting + * Object being returned. + * <p> + * This method will then drain (and discard) all the remaining content + * available from either the input and error streams of the resulting + * connection (which should permit connection keepalives). + * + * @param baseUrl the constant part of the URL to be accessed. + * @param object the object to serialize and send in the POST body + * @param params an array of String values, that contain an alternation of + * parameter name, and parameter values. + * @return the de-serialized value sent by the remote endpoint. + * @throws IOException if the connection fails. + * @throws ClassNotFoundException if the data sent from the remote endpoint + * cannot be deserialized to a class locally known. + */ + public Object rpcCall(String baseUrl, Serializable object, + String... params) throws IOException, ClassNotFoundException { + HttpPost request = new HttpPost(buildUrl(baseUrl, params)); + request.setHeader("Content-Type", "application/octet-stream"); + // Set up the entity to send to the server. + SerializableEntity entity = new SerializableEntity(object); + entity.setChunked(true); + request.setEntity(entity); + + // Now run the request + return new RequestExecutor<>(this) + .runObjectRequest(request, ObjectInputStream::readObject); + } + + protected static class RequestExecutor<T> { + private WebUtils webUtils; + + RequestExecutor(WebUtils webUtils) { + this.webUtils = webUtils; + } + + public T runRequest(HttpUriRequest request, CheckedRequestConsumer<T> consumer) throws IOException { + CloseableHttpResponse response = webUtils.execute(request); + try { + long code = response.getStatusLine().getStatusCode(); + + if (code == HttpURLConnection.HTTP_OK) { + // try to get more details + return consumer.run(response); + } else { + // some problem -> try to get more details + String message = response.getStatusLine().getReasonPhrase(); + throw new IOException(code + + (message != null ? " (" + message + ")" : "") + + " Remote connection failed."); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } finally { + // make sure the connection is drained, to allow connection keepalive + response.close(); + } + } + + public T runObjectRequest(HttpUriRequest request, final CheckedObjectInputStreamConsumer<T> consumer) throws IOException { + return runRequest(request, (CloseableHttpResponse response) -> { + InputStream contentInputStream = null; + try { + contentInputStream = response.getEntity().getContent(); + return consumer.run(new ObjectInputStream(contentInputStream)); + } finally { + contentInputStream.close(); + } + }); + } + + + public interface CheckedRequestConsumer<T> { + T run(CloseableHttpResponse response) throws IOException, ClassNotFoundException; + } + + public interface CheckedObjectInputStreamConsumer<T> { + T run(ObjectInputStream response) throws IOException, ClassNotFoundException; + } + } + + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |