/** * Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * icense version 2 and the aforementioned licenses. * * This program 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 General * Public License for more details. */ package org.n52.ses.common.environment; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.muse.core.platform.mini.MiniIsolationLayer; import org.apache.muse.core.platform.mini.MiniServlet; import org.apache.muse.util.xml.XmlUtils; import org.apache.muse.ws.addressing.soap.SoapConstants; import org.apache.muse.ws.addressing.soap.SoapFault; import org.apache.muse.ws.addressing.soap.SoapUtils; import org.n52.ses.api.ISESFilePersistence; import org.n52.ses.common.environment.handler.GetRequestHandler; import org.n52.ses.common.environment.handler.GetCapabilitiesHandler; import org.n52.ses.common.environment.handler.WSDLProvisionHandler; import org.n52.ses.common.environment.handler.XSDProvisionHandler; import org.n52.ses.requestlogger.RequestLoggerWrapper; import org.n52.ses.startupinit.StartupInitServlet; import org.n52.ses.util.common.ConfigurationRegistry; import org.n52.ses.wsbr.RegisterPublisher; import org.n52.ses.wsn.SESNotificationProducer; import org.n52.ses.wsn.SESSubscriptionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Extended Servlet with support for shutdown and wsdl provision. * * @author Matthes Rieke * */ public class SESMiniServlet extends MiniServlet { private static final Logger logger = LoggerFactory.getLogger(SESMiniServlet.class); private static final long serialVersionUID = 1L; private String landingPage; private MiniIsolationLayer sesIsolationLayer; private List<GetRequestHandler> getRequestHandlers = new ArrayList<GetRequestHandler>(); private static int minimumContentLengthForGzip = 500000; private static RequestLoggerWrapper loggerInst; private static final AtomicBoolean firstResponsePrint = new AtomicBoolean(true); public SESMiniServlet() { logger.info(readFileContents("/ses_version_info.txt")); this.getRequestHandlers.add(new GetCapabilitiesHandler()); this.getRequestHandlers.add(new WSDLProvisionHandler()); this.getRequestHandlers.add(new XSDProvisionHandler()); } private String readFileContents(String string) { InputStream is = getClass().getResourceAsStream(string); if (is != null) { Scanner sc = new Scanner(is); StringBuilder sb = new StringBuilder(); while (sc.hasNext()) { sb.append(sc.nextLine()); sb.append(System.getProperty("line.separator")); } sb.deleteCharAt(sb.length()-1); sc.close(); return sb.toString(); } return null; } @Override public void destroy() { if (ConfigurationRegistry.getInstance() != null) { ConfigurationRegistry.getInstance().shutdown(); } super.destroy(); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { synchronized (SESMiniServlet.class) { if (this.sesIsolationLayer == null) { this.sesIsolationLayer = createIsolationLayer(request, getServletContext()); } } InputStream input = request.getInputStream(); Document soapRequest = null; long time = System.currentTimeMillis(); try { soapRequest = XmlUtils.createDocument(input); } catch (Exception error) { logger.warn(error.getMessage(), error); SoapFault fault = SoapUtils.convertToFault(error); response.setContentType("application/soap+xml; charset=utf-8"); printResponse(request, response, createSoapFaultEnvelope(fault)); return; } handleSoapRequest(request, response, soapRequest); if (RequestLoggerWrapper.isActive()) { if (loggerInst == null) { loggerInst = RequestLoggerWrapper.getInstance(); } if (loggerInst != null) loggerInst.logRequest(time, soapRequest); } } private String createSoapFaultEnvelope(SoapFault fault) { Document response = XmlUtils.createDocument(); Element soap = XmlUtils.createElement(response, SoapConstants.ENVELOPE_QNAME); response.appendChild(soap); Element body = XmlUtils.createElement(response, SoapConstants.BODY_QNAME); soap.appendChild(body); Element result = (Element) response.importNode(fault.toXML(), true); body.appendChild(result); return XmlUtils.toString(response); } private void handleSoapRequest(HttpServletRequest request, HttpServletResponse response, Document soapRequest) throws IOException { Document soapResponse; try { soapResponse = this.sesIsolationLayer.handleRequest(soapRequest); } catch (RuntimeException e) { throw new IOException(e); } /* * is null? return a http response code. * change made by Matthes Rieke <m.rieke@uni-muenster.de> */ if (soapResponse == null) { response.setStatus(HttpServletResponse.SC_NO_CONTENT); } else { //TODO check support of ' ; charset=utf-8' response.setContentType("application/soap+xml; charset=utf-8"); printResponse(request, response, XmlUtils.toString(soapResponse)); } } private boolean clientSupportsGzip(HttpServletRequest request) { String header = request.getHeader("Accept-Encoding"); if (header != null && !header.isEmpty()) { String[] split = header.split(","); for (String string : split) { if (string.equalsIgnoreCase("gzip")) { return true; } } } return false; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ConfigurationRegistry conf = ConfigurationRegistry.getInstance(); for (GetRequestHandler handler : this.getRequestHandlers) { if (handler.canHandle(req)) { try { printResponse(req, resp, handler.handleRequest(req, resp, conf, this.sesIsolationLayer)); } catch (Exception e) { throw new ServletException(e); } return; } } provideLandingPage(req, resp, conf); } private void provideLandingPage(HttpServletRequest req, HttpServletResponse resp, ConfigurationRegistry conf) throws IOException, UnsupportedEncodingException { /* * return landing page */ if (this.landingPage != null) { resp.setContentType("text/html"); synchronized (this) { printResponse(req, resp, this.landingPage); } return; } InputStreamReader isr = new InputStreamReader(getClass().getResourceAsStream("/landing_page.html")); BufferedReader br = new BufferedReader(isr); StringBuilder sb = new StringBuilder(); while (br.ready()) { sb.append(br.readLine()); } String html = sb.toString(); String reqUrl = URLDecoder.decode(req.getRequestURL().toString(), (req.getCharacterEncoding() == null ? Charset.defaultCharset().name() : req.getCharacterEncoding())); html = html.replace("[SES_URL]", reqUrl.substring(0, reqUrl.indexOf(req.getContextPath())) + req.getContextPath()); /* * check if we are init yet */ String sesPortTypeUrl, subMgrUrl, prmUrl = ""; if (conf != null) { String defaulturi = conf.getEnvironment().getDefaultURI().substring(0, conf.getEnvironment().getDefaultURI().lastIndexOf("/services")); sesPortTypeUrl = defaulturi + "/services/" + SESNotificationProducer.CONTEXT_PATH; subMgrUrl = defaulturi + "/services/" + SESSubscriptionManager.CONTEXT_PATH; prmUrl = defaulturi + "/services/" + RegisterPublisher.RESOURCE_TYPE; conf.setSubscriptionManagerWsdl(subMgrUrl + "?wsdl"); html = html.replace("<p id=\"ses-status\"><p>", "<p style=\"color:#0f0\">The service is active and available.</p>"); html = html.replace("[GET_CAPS]", StringEscapeUtils.escapeHtml4(StartupInitServlet.getGetCapabilitiesRequest(sesPortTypeUrl))); /* * replace the url */ synchronized (this) { if (this.landingPage == null) { this.landingPage = html.replace("[SES_PORT_TYPE_URL]", sesPortTypeUrl); this.landingPage = this.landingPage.replace("[SUB_MGR_URL]", subMgrUrl); this.landingPage = this.landingPage.replace("[PRM_URL]", prmUrl); resp.setContentType("text/html"); printResponse(req, resp, this.landingPage); } } } else { /* * we do not have the config, warn the user */ html = html.replace("<p id=\"ses-status\"><p>", "<p style=\"color:#f00\">The service is currently not available due to unfinished or failed initialization.</p>"); resp.setContentType("text/html"); printResponse(req, resp, html); } } private void printResponse(HttpServletRequest request, HttpServletResponse response, String string) throws IOException { int contentLength = string.getBytes("UTF-8").length; if (firstResponsePrint.getAndSet(false)) { ConfigurationRegistry conf = ConfigurationRegistry.getInstance(); if (conf == null) { firstResponsePrint.getAndSet(true); } else { minimumContentLengthForGzip = Integer.parseInt(conf.getPropertyForKey( ConfigurationRegistry.MINIMUM_GZIP_SIZE)); } } // compressed response if (contentLength > minimumContentLengthForGzip && clientSupportsGzip(request)) { response.addHeader("Content-Encoding", "gzip"); GZIPOutputStream gzip = new GZIPOutputStream(response.getOutputStream(), contentLength); String type = response.getContentType(); if (!type.contains("charset")) { response.setContentType(type + "; charset=utf-8"); } gzip.write(string.getBytes(Charset.forName("UTF-8"))); gzip.flush(); gzip.finish(); } // uncompressed response else { response.setContentLength(contentLength); response.setCharacterEncoding("UTF-8"); PrintWriter writer = response.getWriter(); writer.write(string); writer.flush(); } } @Override protected MiniIsolationLayer createIsolationLayer( HttpServletRequest request, ServletContext context) { MiniIsolationLayer isolationLayer = new SESMiniIsolationLayer(request, context); isolationLayer.initialize(); ConfigurationRegistry.getInstance().setFilePersistence((ISESFilePersistence) isolationLayer.getRouter().getPersistence()); return isolationLayer; } }