Java tutorial
/******************************************************************************* * Copyright (c) 2009, 2016 GreenVulcano ESB Open Source Project. * All rights reserved. * * This file is part of GreenVulcano ESB. * * GreenVulcano ESB 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, either version 3 of the License, or * (at your option) any later version. * * GreenVulcano ESB 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 GreenVulcano ESB. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package it.greenvulcano.gvesb.adapter.http.mapping; import it.greenvulcano.configuration.XMLConfig; import it.greenvulcano.configuration.XMLConfigException; import it.greenvulcano.gvesb.adapter.http.HttpServletMapping; import it.greenvulcano.gvesb.adapter.http.HttpServletTransactionManager; import it.greenvulcano.gvesb.adapter.http.exc.GVRequestException; import it.greenvulcano.gvesb.adapter.http.exc.InboundHttpResponseException; import it.greenvulcano.gvesb.adapter.http.formatters.FormatterManager; import it.greenvulcano.gvesb.adapter.http.formatters.handlers.GVTransactionInfo; import it.greenvulcano.gvesb.adapter.http.utils.AdapterHttpConstants; import it.greenvulcano.gvesb.adapter.http.utils.AdapterHttpExecutionException; import it.greenvulcano.gvesb.adapter.http.utils.AdapterHttpInitializationException; import it.greenvulcano.gvesb.adapter.http.utils.DumpUtils; import it.greenvulcano.gvesb.buffer.GVBuffer; import it.greenvulcano.gvesb.buffer.GVException; import it.greenvulcano.gvesb.buffer.GVPublicException; import it.greenvulcano.gvesb.core.pool.GreenVulcanoPool; import it.greenvulcano.gvesb.core.pool.GreenVulcanoPoolManager; import it.greenvulcano.gvesb.log.GVBufferMDC; import it.greenvulcano.gvesb.log.GVFormatLog; import it.greenvulcano.log.NMDC; import it.greenvulcano.util.xml.XMLUtils; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * RESTHttpServletMapping class * * @version 3.5.0 July 20, 2014 * @author GreenVulcano Developer Team * * */ public class RESTHttpServletMapping implements HttpServletMapping { private static Logger logger = org.slf4j.LoggerFactory.getLogger(RESTHttpServletMapping.class); private HttpServletTransactionManager transactionManager = null; private String action = null; private boolean dump = false; private String responseContentType = null; private String responseCharacterEncoding = null; private List<PatternResolver> operationMappings = new ArrayList<PatternResolver>(); static private class PatternResolver { private String pattern; private String method; private String service; private String system; private String operation; private boolean extractHdr; private List<String> propNames = new ArrayList<String>(); private List<Pattern> patterns = new ArrayList<Pattern>(); public PatternResolver() { // do nothing } public void init(Node node) throws AdapterHttpInitializationException { try { this.pattern = XMLConfig.get(node, "@pattern"); if ((pattern == null) || "".equals(pattern)) { throw new AdapterHttpInitializationException( "RESTHttpServletMapping - Error initializing Pattern: empty"); } this.method = XMLConfig.get(node, "@method"); if ((method == null) || "".equals(method)) { throw new AdapterHttpInitializationException( "RESTHttpServletMapping - Error initializing Pattern[" + pattern + "]: empty @method"); } this.service = XMLConfig.get(node, "@service"); if ((service == null) || "".equals(service)) { throw new AdapterHttpInitializationException( "RESTHttpServletMapping - Error initializing Pattern[" + method + "#" + pattern + "]: empty @service"); } this.system = XMLConfig.get(node, "@system", GVBuffer.DEFAULT_SYS); this.operation = XMLConfig.get(node, "@operation"); if ((operation == null) || "".equals(operation)) { throw new AdapterHttpInitializationException( "RESTHttpServletMapping - Error initializing Pattern[" + method + "#" + pattern + "]: empty @operation"); } this.extractHdr = XMLConfig.getBoolean(node, "@extract-headers", false); } catch (XMLConfigException exc) { throw new AdapterHttpInitializationException( "RESTHttpServletMapping - Error initializing Pattern: error reading configuration", exc); } compile(); } private void compile() throws AdapterHttpInitializationException { logger.debug("Compile - BEGIN"); logger.debug("Pattern: " + method + "#" + pattern); String locPattern = pattern; if (locPattern.startsWith("/")) { locPattern = locPattern.substring(1); } String[] list = locPattern.split("/"); for (int i = 0; i < list.length; i++) { String elem = list[i].trim(); if (!"".equals(elem)) { logger.debug("Element: " + elem); String pN = elem.split("=")[0].trim(); String p = elem.split("=")[1].trim(); propNames.add(pN); patterns.add(Pattern.compile(p)); logger.debug("[" + pN + "]=[" + p + "]"); } } logger.debug("Compile - END"); if (patterns.isEmpty()) { throw new AdapterHttpInitializationException("RESTHttpServletMapping - Error initializing Pattern[" + method + "#" + pattern + "]: empty"); } } public String match(HttpServletRequest request, String methodName, String path, GVBuffer data) throws AdapterHttpExecutionException { try { logger.debug("Checking [" + method + "#" + pattern + "] on [" + methodName + "#" + path + "]"); if (!method.equalsIgnoreCase(methodName)) { logger.debug("Pattern [" + method + "#" + pattern + "] NOT matched"); return null; } List<String> values = new ArrayList<String>(); String locPath = path; if (locPath.startsWith("/")) { locPath = locPath.substring(1); } String[] parts = locPath.split("/"); if (parts.length == patterns.size()) { for (int i = 0; i < parts.length; i++) { Matcher m = patterns.get(i).matcher(parts[i]); if (m.matches()) { values.add(parts[i]); } else { logger.debug("Pattern [" + method + "#" + pattern + "] NOT matched"); return null; } } data.setService(service); data.setSystem(system); for (int i = 0; i < propNames.size(); i++) { data.setProperty(propNames.get(i), values.get(i)); } logger.debug("Pattern [" + method + "#" + pattern + "] matched"); return operation; } logger.debug("Pattern [" + method + "#" + pattern + "] NOT matched"); return null; } catch (Exception exc) { throw new AdapterHttpExecutionException( "RESTHttpServletMapping - Error evaluating Pattern[" + method + "#" + pattern + "]", exc); } } public boolean isExtractHdr() { return this.extractHdr; } @Override public String toString() { return method + "#" + pattern + " -> " + service + "/" + system + "/" + operation; } } /** * @param transactionManager * @param formatterMgr * @param configurationNode * @param configurationFile * @throws AdapterHttpInitializationException */ public void init(HttpServletTransactionManager transactionManager, FormatterManager formatterMgr, Node configurationNode) throws AdapterHttpInitializationException { this.transactionManager = transactionManager; try { action = XMLConfig.get(configurationNode, "@Action"); dump = XMLConfig.getBoolean(configurationNode, "@dump-in-out", false); responseContentType = XMLConfig.get(configurationNode, "@RespContentType", AdapterHttpConstants.APPXML_MIMETYPE_NAME); responseCharacterEncoding = XMLConfig.get(configurationNode, "@RespCharacterEncoding", "UTF-8"); NodeList opMaps = XMLConfig.getNodeList(configurationNode, "OperationMappings/Mapping"); for (int i = 0; i < opMaps.getLength(); i++) { Node opM = opMaps.item(i); operationMappings.add(buildPatternResolver(opM)); } } /*catch (AdapterHttpInitializationException exc) { throw exc; }*/ catch (Exception exc) { logger.error("RESTHttpServletMapping - Error initializing action '" + action + "'", exc); throw new AdapterHttpInitializationException( "RESTHttpServletMapping - Error initializing action '" + action + "'", exc); } } /** * @param req * @param resp * @return if request handling was successful * @throws InboundHttpResponseException */ public void handleRequest(String methodName, HttpServletRequest req, HttpServletResponse resp) throws InboundHttpResponseException { logger.debug("handleRequest start"); long startTime = System.currentTimeMillis(); GVTransactionInfo transInfo = new GVTransactionInfo(); Throwable exception = null; GVBuffer response = null; try { if (dump) { StringBuffer sb = new StringBuffer(); DumpUtils.dump(req, sb); logger.info(sb.toString()); } String path = req.getPathInfo(); if (path == null) { path = "/"; } String query = req.getQueryString(); if (query == null) { query = ""; } GVBuffer request = new GVBuffer(); String operationType = null; PatternResolver pr = null; Iterator<PatternResolver> i = operationMappings.iterator(); while (i.hasNext()) { pr = i.next(); operationType = pr.match(req, methodName, path, request); if (operationType != null) { break; } } if (operationType == null) { logger.error(action + " - handleRequest - Error while handling request parameters: unable to decode requested operation [" + methodName + "#" + path + "]"); resp.sendError(400, "Unable to decode the requested operation [" + methodName + "#" + path + "]"); } transInfo.setService(request.getService()); transInfo.setSystem(request.getSystem()); transInfo.setId(request.getId()); transInfo.setOperation(operationType); request.setProperty("HTTP_ACTION", action); request.setProperty("HTTP_PATH", path); request.setProperty("HTTP_QUERY", query); request.setProperty("HTTP_METHOD", methodName); // get remote transport address... String remAddr = req.getRemoteAddr(); request.setProperty("HTTP_REMOTE_ADDR", (remAddr != null ? remAddr : "")); parseRequest(req, methodName, pr, request); GVBufferMDC.put(request); NMDC.setOperation(operationType); logger.info(GVFormatLog.formatBEGINOperation(request).toString()); transactionManager.begin(request.getService(), operationType); response = executeService(operationType, request); if (response.getPropertyNamesSet().contains("HTTP_FORCE_TX_ROLLBACK")) { transInfo.setErrorCode(-1); } else { transactionManager.commit(transInfo, true); manageHttpResponse(response, resp); transactionManager.commit(transInfo, false); } logger.debug("handleRequest stop"); } catch (Throwable exc) { exception = exc; transInfo.setErrorCode(-1); logger.error("handleRequest - Service request failed", exc); try { if (exc.getMessage().contains("GV_SERVICE_POLICY_ERROR")) { resp.sendError(HttpServletResponse.SC_FORBIDDEN); } else { resp.sendError(500, "" + exc); } } catch (IOException exc1) { throw new InboundHttpResponseException("GVHTTP_INBOUND_HTTP_RESPONSE_ERROR", new String[][] { { "errorName", "" + exc1 } }, exc1); } } finally { if (transInfo.isError()) { try { transactionManager.rollback(transInfo, false); } catch (Exception exc) { logger.error("handleRequest - Transaction failed: " + exc); } } long endTime = System.currentTimeMillis(); long totalTime = endTime - startTime; GVFormatLog gvFormatLog = null; if (exception != null) { gvFormatLog = GVFormatLog.formatENDOperation(exception, totalTime); } else { if (response != null) { gvFormatLog = GVFormatLog.formatENDOperation(response, totalTime); } else { gvFormatLog = GVFormatLog.formatENDOperation(totalTime); } } logger.info(gvFormatLog.toString()); } } /** * @param req * @param request * @throws GVException */ private void parseRequest(HttpServletRequest req, String methodName, PatternResolver pr, GVBuffer request) throws GVException { try { Map<String, String[]> params = req.getParameterMap(); Iterator<String> i = params.keySet().iterator(); while (i.hasNext()) { String n = i.next(); String v = params.get(n)[0]; request.setProperty(n, ((v != null) && !"".equals(v)) ? v : "NULL"); } String ct = Optional.ofNullable(req.getContentType()).orElse(""); request.setProperty("HTTP_REQ_CONTENT_TYPE", ct.isEmpty() ? ct : "NULL"); String acc = req.getHeader("Accept"); request.setProperty("HTTP_REQ_ACCEPT", (acc != null) ? acc : "NULL"); if (methodName.equals("POST") || methodName.equals("PUT")) { if (!ct.startsWith(AdapterHttpConstants.URLENCODED_MIMETYPE_NAME)) { Object requestContent = IOUtils.toByteArray(req.getInputStream()); if (ct.startsWith(AdapterHttpConstants.APPXML_MIMETYPE_NAME) || ct.startsWith(AdapterHttpConstants.APPJSON_MIMETYPE_NAME) || ct.startsWith("text/")) { /* GESTIRE ENCODING!!! */ requestContent = new String((byte[]) requestContent); } request.setObject(requestContent); } } if (pr.isExtractHdr()) { XMLUtils parser = null; try { parser = XMLUtils.getParserInstance(); Document doc = parser.newDocument("Hdr"); Element root = doc.getDocumentElement(); Enumeration<?> hn = req.getHeaderNames(); while (hn.hasMoreElements()) { Element h = parser.insertElement(root, "h"); String name = (String) hn.nextElement(); String val = req.getHeader(name); parser.setAttribute(h, "n", name); parser.setAttribute(h, "v", val); } request.setProperty("HTTP_REQ_HEADERS", parser.serializeDOM(doc, true, false)); } finally { XMLUtils.releaseParserInstance(parser); } } } catch (Exception exc) { throw new AdapterHttpExecutionException("RESTHttpServletMapping - Error parsing request data", exc); } } @Override public boolean isDumpInOut() { return dump; } /** * */ public void destroy() { transactionManager = null; operationMappings.clear(); } /** * @return the servlet action */ public String getAction() { return action; } /** * @return the <code>GVBuffer</code> response * @throws AdapterHttpInitializationException */ /*public GVBuffer getResponse() { return response; }*/ private PatternResolver buildPatternResolver(Node n) throws AdapterHttpInitializationException { PatternResolver pr = new PatternResolver(); pr.init(n); return pr; } /** * Invoke GVCore object method corresponding to the specified * operationType passing it the <code>GVBuffer</code> object * <code>gvdInput</code> as input. Returns an <code>GVBuffer</code> object * encapsulating response. * * @param operationType * the type of communication paradigm to be used. * @param gvdInput * the input <code>GVBuffer</code> object. * @return an <code>GVBuffer</code> object encapsulating GVCore * response * @throws GVRequestException * if service request to GVConnector fails. */ private GVBuffer executeService(String operationType, GVBuffer gvInput) throws GVRequestException, GVPublicException { logger.info("BEGIN - Perform Remote Call(GVCore) - Operation(" + operationType + ")"); GVBuffer gvOutput = null; String status = "OK"; long startTime = System.currentTimeMillis(); long endTime = 0; long totalTime = 0; NMDC.push(); try { GreenVulcanoPool greenVulcanoPool = GreenVulcanoPoolManager.instance() .getGreenVulcanoPool(AdapterHttpConstants.SUBSYSTEM) .orElseGet(GreenVulcanoPoolManager::getDefaultGreenVulcanoPool); if (greenVulcanoPool == null) { throw new InboundHttpResponseException("GVHTTP_GREENVULCANOPOOL_NOT_CONFIGURED"); } try { gvOutput = greenVulcanoPool.forward(gvInput, operationType); return gvOutput; } catch (Exception exc) { status = "FAILED"; logger.error("FAILED", exc); throw exc; } finally { NMDC.pop(); endTime = System.currentTimeMillis(); totalTime = endTime - startTime; logger.info("END - Perform Remote Call(GVCore) - Operation(" + operationType + ") - ExecutionTime (" + totalTime + ") - Status: " + status); } } catch (GVPublicException exc) { throw exc; } catch (Throwable exc) { logger.error("executeServiceGVC - Runtime error while invoking GVCore: ", exc); throw new GVRequestException("GVHTTP_RUNTIME_ERROR", new String[][] { { "phase", "invoking GVCore" }, { "errorName", "" + exc } }, exc); } } /** * Handle GreenVulcano response to service request from external systems * communicating via HTTP. * * @throws InboundHttpResponseException * if any error occurs. */ @SuppressWarnings("deprecation") private void manageHttpResponse(GVBuffer response, HttpServletResponse resp) throws InboundHttpResponseException { logger.debug("manageHttpResponse start"); String respCharacterEncoding = null; try { String respStatusCode = response.getProperty("HTTP_RESP_STATUS_CODE"); String respStatusMsg = response.getProperty("HTTP_RESP_STATUS_MSG"); if (respStatusCode != null) { if (respStatusMsg == null) { resp.setStatus(Integer.parseInt(respStatusCode)); } else { //resp.sendError(Integer.parseInt(respStatusCode), respStatusMsg); resp.setStatus(Integer.parseInt(respStatusCode), respStatusMsg); } } /*Map<String, String> headerMap = (Map<String, String>) environment.get(AdapterHttpConstants.ENV_KEY_HTTP_HEADER); if (headerMap != null) { for (Entry<String, String> entry : headerMap.entrySet()) { String paramName = entry.getKey(); String value = entry.getValue(); resp.setHeader(paramName, value); } }*/ Object data = response.getObject(); if (data != null) { respCharacterEncoding = response.getProperty("HTTP_RESP_CHAR_ENCODING"); if (respCharacterEncoding == null) { respCharacterEncoding = responseCharacterEncoding; } String respContentType = response.getProperty("HTTP_RESP_CONTENT_TYPE"); if (respContentType == null) { respContentType = responseContentType; } String fileName = response.getProperty("HTTP_RESP_FILE_NAME"); if ((fileName != null) && !"".equals(fileName)) { int fileSize = -1; if (data instanceof byte[]) { fileSize = ((byte[]) data).length; } else { throw new InboundHttpResponseException( "Invalid GVBuffer content: " + data.getClass().getName()); } setRespDownloadHeaders(resp, respContentType, fileName, fileSize); } else { setRespContentTypeAndCharset(resp, respContentType, respCharacterEncoding); } OutputStream out = resp.getOutputStream(); if (respContentType.equals(AdapterHttpConstants.APPXML_MIMETYPE_NAME) || respContentType.equals(AdapterHttpConstants.APPJSON_MIMETYPE_NAME) || respContentType.startsWith("text/")) { if (data instanceof byte[]) { IOUtils.write((byte[]) data, out); } else if (data instanceof String) { IOUtils.write((String) data, out, respCharacterEncoding); } else if (data instanceof Node) { XMLUtils.serializeDOMToStream_S((Node) data, out, respCharacterEncoding, false, false); } else { throw new InboundHttpResponseException( "Invalid GVBuffer content: " + data.getClass().getName()); } } else { if (data instanceof byte[]) { IOUtils.write((byte[]) data, out); } else { throw new InboundHttpResponseException( "Invalid GVBuffer content: " + data.getClass().getName()); } } out.flush(); out.close(); } if (dump) { StringBuffer sb = new StringBuffer(); DumpUtils.dump(resp, sb); logger.debug(sb.toString()); } } catch (UnsupportedEncodingException exc) { logger.error("manageResponse - Can't encode response to invoking system using encoding " + respCharacterEncoding + ": " + exc); throw new InboundHttpResponseException("GVHTTP_CHARACTER_ENCODING_ERROR", new String[][] { { "encName", respCharacterEncoding }, { "errorName", "" + exc } }, exc); } catch (Exception exc) { logger.error("manageResponse - Can't send response to invoking system: " + exc); throw new InboundHttpResponseException("GVHTTP_INBOUND_HTTP_RESPONSE_ERROR", new String[][] { { "errorName", "" + exc } }, exc); } finally { logger.debug("manageHttpResponse stop"); } } /** * Sets content type and charset header fields of the servlet response. * * @param resp * An HttpServletResponse object * @param contentType * A string containing the declared response's content type * @param charset * A string containing the declared response's charset */ private void setRespContentTypeAndCharset(HttpServletResponse resp, String contentType, String charset) { resp.setContentType(contentType); resp.setCharacterEncoding(charset); } /** * Sets header fields for file download. * * @param resp * An HttpServletResponse object * @param contentType * A string containing the declared response's content type * @param fileName * A string containing the downloaded file name * @param fileSize * A string containing the downloaded file size */ private void setRespDownloadHeaders(HttpServletResponse resp, String contentType, String fileName, int fileSize) { resp.setContentType(contentType); resp.setContentLength(fileSize); resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); resp.setHeader("Connection", "close"); resp.setHeader("Expires", "-1"); resp.setHeader("Pragma", "no-cache"); resp.setHeader("Cache-Control", "no-cache"); } }