Java tutorial
/* * Copyright (c) 2004-2012 The YAWL Foundation. All rights reserved. * The YAWL Foundation is a collaboration of individuals and * organisations who are committed to improving workflow technology. * * This file is part of YAWL. YAWL is free software: you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation. * * YAWL is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with YAWL. If not, see <http://www.gnu.org/licenses/>. */ package org.yawlfoundation.yawl.engine.interfce; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.concurrent.FutureCallback; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.cluster.OriginalYawlEngineApplication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.metrics.Metric; import java.io.*; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class is used by clients and servers to execute GET and POST requests * across the YAWL interfaces. Note that since v2.0up4 (12/08) all requests are sent as * POSTS - increases efficiency, security and allows 'extended' chars to be included. * * @author Lachlan Aldred * Date: 22/03/2004 * Time: 17:49:42 * * @author Michael Adams (refactored for v2.0, 06/2008; and again 12/2008 & 04/2010) */ public class Interface_Client { // allows the prevention of socket reads from blocking indefinitely private static int READ_TIMEOUT = 0; // default: wait indefinitely /** * Executes a HTTP POST request on the url specified. * * @param urlStr the URL to send the POST to * @param paramsMap a set of attribute-value pairs that make up the posted data * @return the result of the POST request * @throws IOException when there's some kind of communication problem */ protected String executePost(String urlStr, Map<String, String> paramsMap) throws IOException { return send(urlStr, paramsMap, true); } protected void asyncExecutePost(String urlStr, Map<String, String> paramsMap) throws IOException { asyncSend(urlStr, paramsMap, true); } public String send(String urlStr, String data) throws IOException { HttpURLConnection connection = initPostConnection(urlStr); sendData(connection, data); String result = getReply(connection.getInputStream()); connection.disconnect(); return result; } /** * Executes a rerouted HTTP GET request as a POST on the specified URL * * @param urlStr the URL to send the GET to * @param paramsMap a set of attribute-value pairs that make up the posted data * @return the result of the request * @throws IOException when there's some kind of communication problem */ protected String executeGet(String urlStr, Map<String, String> paramsMap) throws IOException { return send(urlStr, paramsMap, false); } /** * Initialises a map for transporting parameters - used by extending classes * @param action the name of the action to take * @param handle the current engine session handle * @return the initialised Map */ protected Map<String, String> prepareParamMap(String action, String handle) { Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("action", action); if (handle != null) paramMap.put("sessionHandle", handle); return paramMap; } /** * Set the read timeout value for future connections * @param timeout the timeout value in milliseconds. A value of -1 (the default) * means a read will wait indefinitely. */ protected void setReadTimeout(int timeout) { READ_TIMEOUT = timeout; } /** * Removes the outermost set of xml tags from a string, if any * @param xml the xml string to strip * @return the stripped xml string */ protected String stripOuterElement(String xml) { if (xml != null) { int start = xml.indexOf('>') + 1; int end = xml.lastIndexOf('<'); if (end > start) { return xml.substring(start, end); } } return xml; } /** * Sends data to the specified url via a HTTP POST, and returns the reply * @param connection the http url connection to send the request to * @param paramsMap a map of attribute=value pairs representing the data to send * @param stripOuterXML true if this was originally a POST request, false if a GET request * @return the response from the url * @throws IOException when there's some kind of communication problem */ protected String send(HttpURLConnection connection, Map<String, String> paramsMap, boolean stripOuterXML) throws IOException { // encode data and send query sendData(connection, encodeData(paramsMap)); //retrieve reply String result = getReply(connection.getInputStream()); connection.disconnect(); if (stripOuterXML) result = stripOuterElement(result); return result; } /** * Initialises a HTTP POST connection * @param urlStr the url to connect to * @return an initialised POST connection * @throws IOException when there's some kind of communication problem */ protected HttpURLConnection initPostConnection(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestProperty("Accept-Charset", "UTF-8"); connection.setReadTimeout(READ_TIMEOUT); // required to ensure the connection is not reused. When not set, spurious // intermittent problems (double posts, missing posts) occur under heavy load. connection.setRequestProperty("Connection", "close"); return connection; } /** * Tests a response message for success or failure * @param message the response message to test * @return true if the response represents success */ public boolean successful(String message) { return (message != null) && (message.length() > 0) && (!message.contains("<failure>")); } /*******************************************************************************/ private final Logger logger = LoggerFactory.getLogger(Interface_Client.class); // PRIVATE METHODS // class RequestForwader { final CloseableHttpClient client = HttpClients.createDefault(); public String forwardRequest(String uri, List<NameValuePair> parameterMap) throws IOException { String result; if (!uri.startsWith("http")) { uri = "http://" + uri; } HttpPost httpPost = new HttpPost(uri); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameterMap, "UTF-8"); httpPost.setEntity(entity); HttpContext httpContext = new BasicHttpContext(); CloseableHttpResponse response; response = client.execute(httpPost, httpContext); result = EntityUtils.toString(response.getEntity()); return result; } CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.createDefault(); public RequestForwader() { httpAsyncClient.start(); } class FutureCallbackWithStartTime implements FutureCallback<HttpResponse> { private long start; public FutureCallbackWithStartTime(long start) { this.start = start; } @Override public void completed(HttpResponse httpResponse) { OriginalYawlEngineApplication.writer .set(new Metric<Number>("time_span", System.currentTimeMillis() - start)); } @Override public void failed(Exception e) { } @Override public void cancelled() { } } public void asyncForwardRequest(String uri, List<NameValuePair> parameterMap) { try { if (!uri.startsWith("http")) { uri = "http://" + uri; } final HttpPost httpPost = new HttpPost(uri); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameterMap, "UTF-8"); httpPost.setEntity(entity); httpAsyncClient.execute(httpPost, new FutureCallbackWithStartTime(System.currentTimeMillis())); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } } private final RequestForwader client = new RequestForwader(); // PRIVATE METHODS // /** * Sends data to the specified url via a HTTP POST, and returns the reply * @param urlStr the url to connect to * @param paramsMap a map of attribute=value pairs representing the data to send * @param post true if this was originally a POST request, false if a GET request * @return the response from the url * @throws IOException when there's some kind of communication problem */ private String send(String urlStr, Map<String, String> paramsMap, boolean post) throws IOException { String p = "http://(.*):"; Pattern pattern = Pattern.compile(p); Matcher matcher = pattern.matcher(urlStr); String host = null; while (matcher.find()) { host = matcher.group(1); } if (!Character.isDigit(host.charAt(0))) { InetAddress address = InetAddress.getByName(host); String a = address.toString(); a = a.substring(a.indexOf('/') + 1); urlStr = urlStr.replace(host, a); } List<NameValuePair> list = new ArrayList<NameValuePair>(); for (String key : paramsMap.keySet()) { BasicNameValuePair pair = new BasicNameValuePair(key, paramsMap.get(key)); list.add(pair); } String result = client.forwardRequest(urlStr, list); if (post) { return stripOuterElement(result); } else { return result; } } private void asyncSend(String urlStr, Map<String, String> paramsMap, boolean post) throws IOException { String p = "http://(.*):"; Pattern pattern = Pattern.compile(p); Matcher matcher = pattern.matcher(urlStr); String host = null; while (matcher.find()) { host = matcher.group(1); } if (!Character.isDigit(host.charAt(0))) { InetAddress address = InetAddress.getByName(host); String a = address.toString(); a = a.substring(a.indexOf('/') + 1); urlStr = urlStr.replace(host, a); } List<NameValuePair> list = new ArrayList<NameValuePair>(); for (String key : paramsMap.keySet()) { BasicNameValuePair pair = new BasicNameValuePair(key, paramsMap.get(key)); list.add(pair); } client.asyncForwardRequest(urlStr, list); } /** * Encodes parameter values for HTTP transport * @param params a map of the data parameter values, of the form * [param1=value1],[param2=value2]... * @return a formatted http data string with the data values encoded */ private String encodeData(Map<String, String> params) { StringBuilder result = new StringBuilder(""); for (String param : params.keySet()) { String value = params.get(param); if (value != null) { if (result.length() > 0) result.append("&"); result.append(param).append("=").append(ServletUtils.urlEncode(value)); } } return result.toString(); } /** * Submits data on a HTTP connection * @param connection a valid, open HTTP connection * @param data the data to submit * @throws IOException when there's some kind of communication problem */ private void sendData(HttpURLConnection connection, String data) throws IOException { OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); out.write(data); out.close(); } /** * Receives a reply from a HTTP submission * @param is the InputStream of a URL or Connection object * @return the stream's contents (ie. the HTTP reply) * @throws IOException when there's some kind of communication problem */ private String getReply(InputStream is) throws IOException { final int BUF_SIZE = 16384; // read reply into a buffered byte stream - to preserve UTF-8 BufferedInputStream inStream = new BufferedInputStream(is); ByteArrayOutputStream outStream = new ByteArrayOutputStream(BUF_SIZE); byte[] buffer = new byte[BUF_SIZE]; // read chunks from the input stream and write them out int bytesRead; while ((bytesRead = inStream.read(buffer, 0, BUF_SIZE)) > 0) { outStream.write(buffer, 0, bytesRead); } outStream.close(); inStream.close(); // convert the bytes to a UTF-8 string return outStream.toString("UTF-8"); } }