sascha@2502: package de.intevation.flys.client.server;
sascha@2502: 
sascha@2504: import de.intevation.artifacts.common.ArtifactNamespaceContext;
sascha@2504: import de.intevation.artifacts.common.utils.ClientProtocolUtils;
sascha@2504: import de.intevation.artifacts.common.utils.JSON;
sascha@2504: import de.intevation.artifacts.common.utils.StringUtils;
sascha@2504: import de.intevation.artifacts.common.utils.XMLUtils;
sascha@2504: import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
sascha@2504: import de.intevation.artifacts.httpclient.http.HttpClient;
sascha@2504: import de.intevation.artifacts.httpclient.http.HttpClientImpl;
sascha@2506: import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
felix@4436: import de.intevation.flys.client.shared.MapUtils;
felix@4436: import de.intevation.flys.client.shared.model.MapConfig;
felix@4433: 
felix@4436: import java.io.IOException;
felix@4436: import java.io.InputStream;
felix@4436: import java.io.OutputStream;
felix@4436: import java.io.UnsupportedEncodingException;
felix@4436: import java.net.URLEncoder;
felix@4436: import java.util.ArrayList;
felix@4436: import java.util.Collections;
christian@4474: import java.util.Enumeration;
christian@4473: import java.util.HashMap;
felix@4436: import java.util.LinkedHashMap;
felix@4436: import java.util.List;
felix@4436: import java.util.Map;
sascha@2506: 
felix@4436: import javax.servlet.ServletException;
felix@4436: import javax.servlet.http.HttpServlet;
felix@4436: import javax.servlet.http.HttpServletRequest;
felix@4436: import javax.servlet.http.HttpServletResponse;
felix@4436: 
felix@4436: import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
felix@4436: import org.apache.commons.httpclient.methods.GetMethod;
felix@4436: import org.apache.log4j.Logger;
felix@4436: import org.w3c.dom.Document;
felix@4436: import org.w3c.dom.Element;
felix@4436: import org.w3c.dom.NodeList;
felix@4433: /*
felix@4433: import java.io.BufferedInputStream;
felix@4433: import java.io.BufferedOutputStream;
felix@4433: import java.io.File;
felix@4433: import java.io.FileInputStream;
felix@4433: import java.io.FileOutputStream;
felix@4433: */
sascha@2519: /* Used by direct API call. -> Enforce GPLv3
sascha@2502: import org.mapfish.print.MapPrinter;
sascha@2502: import org.mapfish.print.output.OutputFactory;
sascha@2502: import org.mapfish.print.output.OutputFormat;
sascha@2502: 
sascha@2502: import org.mapfish.print.utils.PJsonObject;
sascha@2905: */
sascha@2502: 
sascha@2502: public class MapPrintServiceImpl
sascha@2502: extends      HttpServlet
sascha@2502: {
sascha@2502:     private static final Logger log =
sascha@2502:         Logger.getLogger(MapPrintServiceImpl.class);
sascha@2502: 
sascha@2506:     protected static class Layer implements Comparable<Layer> {
sascha@2504: 
sascha@2506:         protected int    pos;
sascha@2506:         protected String url;
sascha@2506:         protected String layers;
sascha@2506:         protected String description;
sascha@2504: 
sascha@2506:         public Layer() {
sascha@2506:         }
sascha@2504: 
sascha@2506:         public boolean setup(Element element) {
sascha@2504: 
sascha@2506:             Element parent = (Element)element.getParentNode();
sascha@2506:             String parentName = parent.getAttribute("name");
sascha@2905:             if (!(parentName.equals("map")
sascha@2506:             ||    parentName.equals("floodmap"))) {
sascha@2506:                 return false;
sascha@2504:             }
sascha@2506: 
sascha@2506:             String ns = ArtifactNamespaceContext.NAMESPACE_URI;
sascha@2506: 
sascha@2506:             String visible = element.getAttributeNS(ns, "visible");
sascha@2506:             String active  = element.getAttributeNS(ns, "active");
sascha@2506: 
sascha@2506:             if (visible.equals("0") || active.equals("0")) {
sascha@2506:                 return false;
sascha@2506:             }
sascha@2506: 
sascha@2506:             url         = element.getAttributeNS(ns, "url");
sascha@2506:             layers      = element.getAttributeNS(ns, "layers");
sascha@2506:             description = element.getAttributeNS(ns, "description");
sascha@2506: 
sascha@2506:             try {
sascha@2506:                 pos = Integer.parseInt(element.getAttributeNS(ns, "pos"));
sascha@2506:             }
sascha@2506:             catch (NumberFormatException nfe) {
sascha@2506:                 return false;
sascha@2506:             }
sascha@2506: 
sascha@2506:             return true;
sascha@2506:         }
sascha@2506: 
sascha@2506:         public Map<String, Object> toMap() {
sascha@2504:             Map<String, Object> layer = new LinkedHashMap<String, Object>();
sascha@2504: 
sascha@2504:             layer.put("type", "WMS");
sascha@2504:             List<Object> subLayers = new ArrayList<Object>(1);
sascha@2506:             subLayers.add(layers);
sascha@2504:             layer.put("layers", subLayers);
sascha@2506:             layer.put("baseURL", url);
sascha@2506:             layer.put("format", "image/png"); // TODO: Make configurable.
sascha@2504: 
sascha@2506:             return layer;
sascha@2506:         }
sascha@2506: 
sascha@2506:         @Override
sascha@2506:         public int compareTo(Layer other) {
sascha@2506:             int d = pos - other.pos;
sascha@2506:             if (d < 0) return -1;
sascha@2506:             return d > 0 ? +1 : 0;
sascha@2506:         }
sascha@2506:     } // class Layer
sascha@2506: 
sascha@2506:     protected static String generateSpec(
sascha@2506:         Document descDocument,
sascha@2508:         MapConfig mapConfig,
sascha@2508:         Double minX, Double minY,
christian@4473:         Double maxX, Double maxY,
christian@4474:         Map<String, Object> pageSpecs
sascha@2506:     ) {
sascha@2506:         Map<String, Object> spec = new LinkedHashMap<String, Object>();
christian@4969:         spec.put("layout",       "A4 landscape");
christian@4474:         spec.put("pageSize",     "A4");
christian@4474:         spec.put("landscape",    "false");
sascha@2506:         spec.put("title",        "FLYS Druck");
sascha@2506:         spec.put("srs",          "EPSG:" + mapConfig.getSrid());
sascha@2506:         spec.put("dpi",          Integer.valueOf(254));
sascha@2508:         spec.put("units",        "m");
sascha@2510:         spec.put("geodaetic",    "true");
sascha@2506:         spec.put("outputFormat", "pdf");
sascha@2506: 
christian@4474:         spec.putAll(pageSpecs);
christian@4474: 
sascha@2506:         String ns = ArtifactNamespaceContext.NAMESPACE_URI;
sascha@2506: 
sascha@2510:         List<Layer> ls = new ArrayList<Layer>();
christian@4472:         Layer l = new Layer();
sascha@2510: 
christian@4472:         NodeList facets = descDocument.getElementsByTagNameNS(ns, "facet");
christian@4472: 
christian@4472:         for (int i = 0, N = facets.getLength(); i < N; ++i) {
christian@4472:             Element element = (Element)facets.item(i);
christian@4472:             if (l.setup(element)) {
christian@4472:                 ls.add(l);
christian@4472:                 l = new Layer();
sascha@2506:             }
sascha@2506:         }
sascha@2506: 
sascha@2506:         // Establish Z order.
sascha@2506:         Collections.sort(ls);
sascha@2506: 
sascha@2506:         List<Object> layers = new ArrayList<Object>(ls.size());
sascha@2506: 
sascha@2510:         for (int i = ls.size()-1; i >= 0; --i) {
sascha@2510:             layers.add(ls.get(i).toMap());
sascha@2504:         }
sascha@2504: 
sascha@2504:         spec.put("layers", layers);
sascha@2504:         spec.put("name", "Name");
sascha@2504: 
sascha@2504:         List<Object> pages = new ArrayList<Object>(1);
sascha@2504: 
sascha@2504: 
sascha@2504:         Map<String, Object> page = new LinkedHashMap<String, Object>();
sascha@2504: 
sascha@2504:         List<Object> bounds = new ArrayList<Object>(4);
sascha@2510:         bounds.add(minX);
sascha@2510:         bounds.add(minY);
sascha@2510:         bounds.add(maxX);
sascha@2510:         bounds.add(maxY);
sascha@2510:         page.put("bbox", bounds);
sascha@2504: 
sascha@2510:         /*
sascha@2510:         bounds.add(Double.valueOf((minX+maxX)*0.5));
sascha@2510:         bounds.add(Double.valueOf((minY+maxY)*0.5));
sascha@2510: 
sascha@2510:         page.put("center", bounds);
sascha@2510:         page.put("scale", Integer.valueOf(50000));
sascha@2510:         */
sascha@2504: 
sascha@2504:         page.put("mapTitle", "FLYS Karte");
sascha@2504:         page.put("comment", "Eine mit FLYS gedruckte Karte.");
sascha@2504:         page.put("rotation", Integer.valueOf(0));
sascha@2504: 
christian@4473:         // This may overwrite default settings above
christian@4474:         page.putAll(pageSpecs);
christian@4473: 
sascha@2504:         pages.add(page);
sascha@2510:         spec.put("pages", pages);
sascha@2510: 
sascha@2510:         List<Object> legends = new ArrayList<Object>(layers.size());
sascha@2510: 
sascha@2510:         for (Layer layer: ls) {
sascha@2510:             Map<String, Object> legend = new LinkedHashMap<String, Object>();
sascha@2510:             List<Object> classes = new ArrayList<Object>(1);
sascha@2510:             Map<String, Object> clazz = new LinkedHashMap<String, Object>();
sascha@2510:             String lgu = MapUtils.getLegendGraphicUrl(layer.url, layer.layers);
sascha@2510:             clazz.put("icon", lgu);
sascha@2510:             clazz.put("name", layer.description);
sascha@2510:             classes.add(clazz);
sascha@2510:             legend.put("classes", classes);
sascha@2510:             legend.put("name", layer.description);
sascha@2510:             legends.add(legend);
sascha@2510:         }
sascha@2510: 
sascha@2510:         spec.put("legends", legends);
sascha@2504: 
sascha@2504:         return JSON.toJSONString(spec);
sascha@2504:     }
sascha@2502: 
sascha@2502: 
sascha@2503:     @Override
sascha@2905:     public void doGet(HttpServletRequest req, HttpServletResponse resp)
sascha@2503:     throws  ServletException, IOException
sascha@2503:     {
sascha@2503:         log.info("MapPrintServiceImpl.doGet");
sascha@2503: 
sascha@2503:         String uuid = req.getParameter("uuid");
sascha@2503: 
sascha@2503:         if (uuid == null || !StringUtils.checkUUID(uuid)) {
sascha@2503:             throw new ServletException("Missing or misspelled UUID");
sascha@2503:         }
sascha@2503: 
sascha@2503:         String minXS = req.getParameter("minx");
sascha@2503:         String maxXS = req.getParameter("maxx");
sascha@2503:         String minYS = req.getParameter("miny");
sascha@2503:         String maxYS = req.getParameter("maxy");
sascha@2503: 
sascha@2508:         Double minX = null;
sascha@2508:         Double maxX = null;
sascha@2508:         Double minY = null;
sascha@2508:         Double maxY = null;
sascha@2503: 
sascha@2508:         if (minXS != null && maxXS != null
sascha@2515:         &&  minYS != null && maxYS != null) {
sascha@2515:             log.debug("all parameters found -> parsing");
sascha@2508:             try {
sascha@2508:                 minX = Double.parseDouble(minXS);
sascha@2508:                 minY = Double.parseDouble(minYS);
sascha@2508:                 maxX = Double.parseDouble(maxXS);
sascha@2508:                 maxY = Double.parseDouble(maxYS);
sascha@2508:             }
sascha@2508:             catch (NumberFormatException nfe) {
sascha@2508:                 throw new ServletException("Misspelled minX, minY, maxX or maxY");
sascha@2508:             }
sascha@2503:         }
sascha@2503: 
sascha@2503:         String mapType = req.getParameter("maptype");
sascha@2503: 
sascha@2508:         if (mapType == null || !mapType.equals("floodmap")) {
sascha@2508:             mapType = "map";
sascha@2503:         }
sascha@2503: 
christian@4473:         // Retrieve print settings from request
christian@4474:         Map<String, Object> pageSpecs = new HashMap<String, Object>();
christian@4474:         Enumeration<String> paramNames = req.getParameterNames();
christian@4474:         while (paramNames.hasMoreElements()) {
christian@4474:             String paramName = paramNames.nextElement();
christian@4474:             if (paramName.startsWith("mapfish-")) {
christian@4474:                 String paramValue = req.getParameter(paramName);
christian@4474:                 pageSpecs.put(paramName.substring(8), paramValue);
christian@4474:             }
christian@4474:         }
christian@4473: 
sascha@2503:         String url = getURL();
sascha@2503: 
sascha@2506:         Document requestOut =
sascha@2506:             ClientProtocolUtils.newOutCollectionDocument(
sascha@2506:                 uuid, mapType, mapType);
sascha@2506:         Document requestDesc =
sascha@2506:             ClientProtocolUtils.newDescribeCollectionDocument(uuid);
sascha@2503: 
sascha@2506:         Document outDocument;
sascha@2506:         Document descDocument;
sascha@2506: 
sascha@2503:         try {
sascha@2503:             HttpClient client = new HttpClientImpl(url);
sascha@2506: 
sascha@2508:             descDocument = (Document)client.doCollectionAction(
sascha@2508:                 requestDesc, uuid, new DocumentResponseHandler());
sascha@2508: 
sascha@2506:             InputStream is = client.collectionOut(
sascha@2506:                 requestOut, uuid, mapType);
sascha@2506: 
sascha@2503:             try {
sascha@2506:                 outDocument = XMLUtils.parseDocument(is);
sascha@2503:             }
sascha@2503:             finally {
sascha@2503:                 is.close();
sascha@2506:                 is = null;
sascha@2503:             }
sascha@2506: 
sascha@2503:         }
sascha@2503:         catch (ConnectionException ce) {
sascha@2503:             log.error(ce);
sascha@2503:             throw new ServletException(ce);
sascha@2503:         }
sascha@2503: 
sascha@2508:         MapConfig mapConfig = MapHelper.parseConfig(outDocument);
sascha@2508: 
sascha@2508:         if (minX == null) {
sascha@2515:             log.debug("parameters missing -> fallback to max extent");
sascha@2508:             String [] parts = mapConfig.getMaxExtent().split("\\s+");
sascha@2508:             if (parts.length < 4) {
sascha@2508:                 throw new ServletException(
sascha@2508:                     "Max extent has less than 4 values");
sascha@2508:             }
sascha@2508:             try {
sascha@2508:                 minX = Double.valueOf(parts[0]);
sascha@2508:                 minY = Double.valueOf(parts[1]);
sascha@2508:                 maxX = Double.valueOf(parts[2]);
sascha@2508:                 maxY = Double.valueOf(parts[3]);
sascha@2508:             }
sascha@2508:             catch (NumberFormatException nfe) {
sascha@2508:                 throw new ServletException(nfe);
sascha@2508:             }
sascha@2508:         }
sascha@2510:         if (log.isDebugEnabled()) {
sascha@2510:             log.debug("minX: " + minX);
sascha@2510:             log.debug("maxX: " + maxX);
sascha@2510:             log.debug("minY: " + minY);
sascha@2510:             log.debug("maxY: " + maxY);
sascha@2510:         }
sascha@2508: 
sascha@2504:         String spec = generateSpec(
sascha@2506:             descDocument,
sascha@2508:             mapConfig,
sascha@2504:             minX, minY,
christian@4473:             maxX, maxY,
christian@4474:             pageSpecs);
sascha@2503: 
sascha@2504:         if (log.isDebugEnabled()) {
sascha@2510:             log.debug("Generated spec:");
sascha@2504:             log.debug(spec);
sascha@2504:             //System.err.println(spec);
sascha@2504:         }
sascha@2503: 
sascha@2504:         producePDF(spec, resp);
sascha@2503:     }
sascha@2503: 
sascha@2503:     protected String getURL() throws ServletException {
sascha@2503:         String url = getServletContext().getInitParameter("server-url");
sascha@2503:         if (url == null) {
sascha@2503:             throw new ServletException("Missing server-url");
sascha@2503:         }
sascha@2503:         return url;
sascha@2503:     }
sascha@2503: 
sascha@2519:     private static final String encode(String s) {
sascha@2519:         try {
sascha@2519:             return URLEncoder.encode(s, "UTF-8");
sascha@2519:         }
sascha@2519:         catch (UnsupportedEncodingException usee) {
sascha@2519:             // Should not happen.
sascha@2519:             return s;
sascha@2519:         }
sascha@2519:     }
sascha@2519: 
sascha@2519:     protected void producePDF(String json, HttpServletResponse resp)
sascha@2519:     throws ServletException, IOException
sascha@2519:     {
sascha@2519:         String printUrl = getInitParameter("print-url");
sascha@2519: 
sascha@2519:         if (printUrl == null) {
sascha@2519:             throw new ServletException("Missing 'print-url' in web.xml");
sascha@2519:         }
sascha@2519: 
sascha@2519:         String url = printUrl + "/print.pdf?spec=" + encode(json);
sascha@2519: 
sascha@2519:         org.apache.commons.httpclient.HttpClient client =
sascha@2519:             new org.apache.commons.httpclient.HttpClient(
sascha@2519:                 new MultiThreadedHttpConnectionManager());
sascha@2519: 
felix@4436:         // FIXME: The request is not authenticated.
felix@4436:         //        Currently this is not a problem because /flys/map-print
felix@4436:         //        is whitelisted in GGInAFilter.
sascha@2519:         GetMethod get = new GetMethod(url);
sascha@2519:         int result = client.executeMethod(get);
sascha@2519:         InputStream in = get.getResponseBodyAsStream();
sascha@2519: 
sascha@2519:         if (in != null) {
sascha@2519:             try {
sascha@2519:                 OutputStream out = resp.getOutputStream();
sascha@2519:                 try {
sascha@2519:                     byte [] buf = new byte[4096];
sascha@2519:                     int r;
sascha@2519:                     while ((r = in.read(buf)) >= 0) {
sascha@2519:                         out.write(buf, 0, r);
sascha@2519:                     }
sascha@2519:                     out.flush();
sascha@2519:                 }
sascha@2519:                 finally {
sascha@2519:                     out.close();
sascha@2519:                 }
sascha@2519:             }
sascha@2519:             finally {
sascha@2519:                 in.close();
sascha@2519:             }
sascha@2519:         }
sascha@2519:     }
sascha@2519: 
sascha@2519:     /* Use this if you want directly call the MapPrinter. Enforces GPLv3!
sascha@2519: 
sascha@2503:     protected MapPrinter getMapPrinter() throws ServletException, IOException {
sascha@2502:         String configPath = getInitParameter("config");
sascha@2502:         if (configPath == null) {
sascha@2502:             throw new ServletException("Missing configuration in web.xml");
sascha@2502:         }
sascha@2502: 
sascha@2502:         File configFile = new File(configPath);
sascha@2502:         if (!configFile.isAbsolute()) {
sascha@2502:             configFile = new File(getServletContext().getRealPath(configPath));
sascha@2502:         }
sascha@2502: 
sascha@2502:         if (!configFile.isFile() || !configFile.canRead()) {
sascha@2502:             throw new ServletException("Cannot read '" + configFile + "'");
sascha@2502:         }
sascha@2502: 
sascha@2502:         return new MapPrinter(configFile);
sascha@2502:     }
sascha@2502: 
sascha@2503:     protected void producePDF(String json, HttpServletResponse resp)
sascha@2503:     throws ServletException, IOException
sascha@2502:     {
sascha@2504:         PJsonObject jsonSpec = MapPrinter.parseSpec(json);
sascha@2502: 
sascha@2502:         MapPrinter printer = getMapPrinter();
sascha@2502: 
sascha@2502:         OutputFormat outputFormat = OutputFactory.create(
sascha@2502:             printer.getConfig(), jsonSpec);
sascha@2502: 
sascha@2502:         resp.setHeader("Content-Disposition", "attachment;filename=print.pdf");
sascha@2502:         resp.setHeader("Content-Type", "application/pdf");
sascha@2502: 
sascha@2502:         // XXX: Streaming the generated PDF directly
sascha@2502:         // to the request out does not work. :-/
sascha@2502:         File tmpFile = File.createTempFile("map-printing", null);
sascha@2502: 
sascha@2502:         try {
sascha@2502:             OutputStream out =
sascha@2502:                 new BufferedOutputStream(
sascha@2502:                 new FileOutputStream(tmpFile));
sascha@2502:             try {
sascha@2502:                 outputFormat.print(printer, jsonSpec, out, "");
sascha@2502:                 out.flush();
sascha@2502:             }
sascha@2502:             catch (Exception e) {
sascha@2502:                 log.error(e);
sascha@2502:                 throw new ServletException(e);
sascha@2502:             }
sascha@2502:             finally {
sascha@2502:                 printer.stop();
sascha@2502:                 out.close();
sascha@2502:             }
sascha@2502:             InputStream in =
sascha@2502:                 new BufferedInputStream(
sascha@2502:                 new FileInputStream(tmpFile));
sascha@2502:             out = resp.getOutputStream();
sascha@2502:             try {
sascha@2502:                 byte [] buf = new byte[4096];
sascha@2502:                 int r;
sascha@2502:                 while ((r = in.read(buf)) >= 0) {
sascha@2502:                     out.write(buf, 0, r);
sascha@2502:                 }
sascha@2502:                 out.flush();
sascha@2502:             }
sascha@2502:             finally {
sascha@2502:                 in.close();
sascha@2502:                 out.close();
sascha@2502:             }
sascha@2502:         }
sascha@2502:         finally {
sascha@2502:             if (tmpFile.exists()) {
sascha@2502:                 tmpFile.delete();
sascha@2502:             }
sascha@2502:         }
sascha@2502:     }
sascha@2519:     */
sascha@2502: }
sascha@2502: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :