changeset 1129:da3c3e286c88

Introduced a MapfileGenerator that creates Mapserver specific mapfiles based on WSPLGEN results read from filesystem. flys-artifacts/trunk@2644 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Mon, 05 Sep 2011 14:04:03 +0000 (2011-09-05)
parents 727c53fd0dc7
children e6dd52342eb7
files flys-artifacts/ChangeLog flys-artifacts/doc/conf/conf.xml flys-artifacts/doc/conf/mapserver/layer.vm flys-artifacts/doc/conf/mapserver/mapfile.vm flys-artifacts/pom.xml flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java
diffstat 9 files changed, 596 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Mon Sep 05 07:17:52 2011 +0000
+++ b/flys-artifacts/ChangeLog	Mon Sep 05 14:04:03 2011 +0000
@@ -1,3 +1,28 @@
+2011-09-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: Added Apache Velocity 1.7 for templating support.
+
+	* doc/conf/conf.xml: Added config options for mapserver/template relevant
+	  stuff.
+
+	* doc/conf/mapserver/mapfile.vm,
+	  doc/conf/mapserver/layer.vm: New. A default mapfile template and a
+	  template used for layers.
+
+	* src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java: New.
+	  This class is used while reading WMS layer relevant information from
+	  filesystem.
+
+	* src/main/java/de/intevation/flys/utils/MapfileGenerator.java: New. This
+	  thread is used for creating mapfiles for Mapserver. The MapfileGenerator
+	  runs in daemon mode (own thread) and creates mapfiles based on WMS
+	  layer relevant information read from filesystem.
+
+	* src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java,
+	  src/main/java/de/intevation/flys/utils/FLYSUtils.java: Moved shapefile
+	  specific XPath expressions from FloodMapState to FLYSUtils which is a
+	  better place to use it in other classes (as MapfileGenerator).
+
 2011-09-05  Ingo Weinzierl <ingo@intevation.de>
 
 	* src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java:
--- a/flys-artifacts/doc/conf/conf.xml	Mon Sep 05 07:17:52 2011 +0000
+++ b/flys-artifacts/doc/conf/conf.xml	Mon Sep 05 14:04:03 2011 +0000
@@ -120,6 +120,16 @@
          creating a Map view.-->
     <floodmap>
         <shapefile-path value="${artifacts.config.dir}/../shapefiles"/>
+        <mapserver>
+            <server path="http://flys-devel.intevation.de/cgi-bin/"/>
+            <mapfile path="${artifacts.config.dir}/../flys.map"/>
+            <templates path="${artifacts.config.dir}/mapserver/"/>
+            <map-template path="mapfile.vm"/>
+        </mapserver>
+
+        <velocity>
+            <logfile path="${artifacts.config.dir}/../velocity_log.log"/>
+        </velocity>
 
         <river name="Saar">
             <srid value="31466"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/layer.vm	Mon Sep 05 14:04:03 2011 +0000
@@ -0,0 +1,23 @@
+LAYER
+    NAME      "$LAYER.getIdentifier()"
+    TYPE      "$LAYER.getType()"
+    DATA      "$LAYER.getIdentifier()/$LAYER.getData()"
+    STATUS    ON
+    TEMPLATE  map.html
+    TOLERANCE 10
+    DUMP      TRUE
+
+    METADATA
+        "wms_title" "$LAYER.getTitle()"
+        "gml_include_items" "all"
+    END
+
+    CLASS
+        NAME "Layer"
+        STYLE
+            SYMBOl 'point'
+            SIZE 5
+            COLOR "#FFFF00"
+        END
+    END
+END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/mapserver/mapfile.vm	Mon Sep 05 14:04:03 2011 +0000
@@ -0,0 +1,40 @@
+MAP
+    NAME "FLYS-Map"
+    STATUS ON
+    SIZE 600 400
+    MAXSIZE 4000
+    EXTENT -1 53 11 58
+    UNITS DD
+    SHAPEPATH $SHAPEFILEPATH
+    FONTSET "fontset.txt"
+    IMAGECOLOR 255 255 255
+    PROJECTION
+        "init=epsg:31466"
+    END
+
+    WEB
+      METADATA
+        "wms_title"             "FLYS Web Map Service"
+        "wms_onlineresource"    "$MAPSERVERURL"
+        "wms_accessconstraints" "none"
+        "wms_fees"              "none"
+        "wms_addresstype"       "postal"
+        "wms_address"           "Any Street"
+        "wms_city"              "Any City"
+        "wms_stateorprovince"   "Any state"
+        "wms_postcode"          "My Postalcode"
+        "wms_country"           "Any Country"
+        "wms_contactperson"     "Any Person"
+        "wms_contactorganization" "Any Orga"
+        "wms_contactelectronicmailaddress" "any-email@example.com"
+        "wms_contactvoicetelephone" "Any's telephone number"
+        "wms_srs" "EPSG:4326 EPSG:31466 EPSG:31467"
+        "wms_feature_info_mime_type" "text/html"
+      END
+    END
+
+    ## Don't change the following lines.
+    #foreach ($LAYER in $LAYERS)
+        $LAYER
+    #end
+END
--- a/flys-artifacts/pom.xml	Mon Sep 05 07:17:52 2011 +0000
+++ b/flys-artifacts/pom.xml	Mon Sep 05 14:04:03 2011 +0000
@@ -120,6 +120,11 @@
       <artifactId>gt-geojson</artifactId>
       <version>2.7.2</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+      <version>1.7</version>
+    </dependency>
   </dependencies>
   <repositories>
     <repository>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java	Mon Sep 05 14:04:03 2011 +0000
@@ -0,0 +1,42 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.File;
+
+
+public class LayerInfo {
+
+    protected String data;
+
+    protected String type;
+    protected String identifier;
+    protected String title;
+
+
+    public LayerInfo(File data, String type, String identifier, String title) {
+        this.data       = data.getName();
+        this.type       = type;
+        this.identifier = identifier;
+        this.title      = title;
+    }
+
+
+    public String getData() {
+        return data;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public String getIdentifier() {
+        return identifier;
+    }
+
+
+    public String getTitle() {
+        return title;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java	Mon Sep 05 07:17:52 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java	Mon Sep 05 14:04:03 2011 +0000
@@ -62,9 +62,6 @@
         System.getProperty("flys.uesk.keep.artifactsdir", "false");
 
 
-    public static final String XPATH_SHAPEFILE_DIR =
-        "/artifact-database/floodmap/shapefile-path/@value";
-
     public static final String WSP_ARTIFACT_UUID = "uesk.wsp.artifact";
 
     public static final String WINFO_WSP_STATE_ID = "state.winfo.waterlevel";
@@ -123,20 +120,6 @@
 
 
     /**
-     * Returns the shapefile path defined in the configuration.
-     *
-     * @return the shapefile path.
-     */
-    protected String getShapefilePath() {
-        String shapePath = (String) Config.getXPath(
-            XPATH_SHAPEFILE_DIR, XPathConstants.STRING);
-        shapePath = Config.replaceConfigDir(shapePath);
-
-        return shapePath;
-    }
-
-
-    /**
      * Returns (and creates if not existing) the directory for storing WSPLEN
      * data for the owner artifact.
      *
@@ -145,7 +128,8 @@
      * @return the directory for WSPLEN data.
      */
     protected File getDirectory(FLYSArtifact artifact) {
-        String shapePath = getShapefilePath();
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_SHAPEFILE_DIR);
 
         File artifactDir = FileTools.getDirectory(
             shapePath, artifact.identifier());
@@ -159,8 +143,10 @@
      * results of WSPLGEN are stored. Should be called in endOfLife().
      */
     protected void removeDirectory(FLYSArtifact artifact) {
-        String shapePath    = getShapefilePath();
-        File   artifactDir = new File(shapePath, artifact.identifier());
+        String shapePath = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_SHAPEFILE_DIR);
+
+        File artifactDir = new File(shapePath, artifact.identifier());
 
         if (artifactDir.exists()) {
             logger.info("Delete directory: " + artifactDir.getAbsolutePath());
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java	Mon Sep 05 07:17:52 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java	Mon Sep 05 14:04:03 2011 +0000
@@ -25,6 +25,24 @@
     public static final String XPATH_RIVER_PROJECTION =
         "/artifact-database/floodmap/river[@name=$name]/srid/@value";
 
+    public static final String XPATH_SHAPEFILE_DIR =
+        "/artifact-database/floodmap/shapefile-path/@value";
+
+    public static final String XPATH_VELOCITY_LOGFILE =
+        "/artifact-database/floodmap/velocity/logfile/@path";
+
+    public static final String XPATH_MAPSERVER_URL =
+        "/artifact-database/floodmap/mapserver/server/@path";
+
+    public static final String XPATH_MAPFILE_PATH =
+        "/artifact-database/floodmap/mapserver/mapfile/@path";
+
+    public static final String XPATH_MAPFILE_TEMPLATE =
+        "/artifact-database/floodmap/mapserver/map-template/@path";
+
+    public static final String XPATH_MAPSERVER_TEMPLATE_PATH =
+        "/artifact-database/floodmap/mapserver/templates/@path";
+
 
     private FLYSUtils() {
     }
@@ -38,6 +56,22 @@
     }
 
 
+    /**
+     * Convinience function to retrieve an XPath as string with replaced config
+     * directory.
+     *
+     * @param xpath The XPath expression.
+     *
+     * @return a string with replaced config directory.
+     */
+    public static String getXPathString(String xpath) {
+        String tmp = Config.getStringXPath(xpath);
+        tmp        = Config.replaceConfigDir(tmp);
+
+        return tmp;
+    }
+
+
     public static KM_MODE getKmRangeMode(FLYSArtifact flys) {
         String mode = flys.getDataAsString("ld_mode");
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java	Mon Sep 05 14:04:03 2011 +0000
@@ -0,0 +1,411 @@
+package de.intevation.flys.utils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+
+import de.intevation.flys.artifacts.model.LayerInfo;
+
+/**
+ * This class iterates over a bunch of directories, searches for meta
+ * information coresponding to shapefiles and creates a mapfile which is used by
+ * a <i>MapServer</i>.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapfileGenerator
+extends Thread
+{
+    public static final String WSPLGEN_RESULT_SHAPE = "wsplgen.shp";
+
+    protected static final long SLEEPTIME = 10 * 1000L; // 10 seconds
+
+    private static Logger logger = Logger.getLogger(MapfileGenerator.class);
+
+    private static MapfileGenerator instance;
+
+    private File mapfile;
+    private File shapefileDirectory;
+
+    private String mapServerUrl;
+    private String templatePath;
+    private String velocityLogfile;
+
+    private VelocityEngine velocityEngine;
+    private boolean lock[];
+
+
+
+    private MapfileGenerator() {
+        lock = new boolean[1];
+    }
+
+
+    /**
+     * A main method which can be used to create a mapfile without a running
+     * artifact server.<br>
+     * <b>NOTE:</b> This method is not implemented yet!
+     *
+     * @param args Arguments.
+     */
+    public static void main(String[] args) {
+        throw new Error("MapfileGenerator.main() is CURRENTLY NOT IMPLEMENTED!");
+    }
+
+
+    /**
+     * Returns the instance of this generator.
+     *
+     * @return the instance.
+     */
+    public static synchronized MapfileGenerator getInstance() {
+        if (instance == null) {
+            instance = new MapfileGenerator();
+            instance.setDaemon(true);
+            instance.start();
+        }
+
+        return instance;
+    }
+
+
+    /**
+     * Triggers the mapfile generation process.
+     */
+    public void update() {
+        synchronized (lock) {
+            logger.debug("update");
+            lock[0] = true;
+            lock.notify();
+        }
+    }
+
+
+    /**
+     * Thread to generate a mapfile.
+     */
+    @Override
+    public void run() {
+        logger.debug("Start MapfileGenerator thread...");
+        try {
+            for (;;) {
+                synchronized (lock) {
+                    while (!lock[0]) {
+                        lock.wait(SLEEPTIME);
+                    }
+                    lock[0] = false;
+                }
+
+                logger.debug("Start sync process now...");
+                generate();
+            }
+        }
+        catch (InterruptedException ie) {
+            logger.debug("MapfileGenerator thread got an interrupt.");
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.debug("Error while mapfile creation: " + fnfe.getMessage());
+        }
+        finally {
+            logger.debug("THREAD END");
+        }
+    }
+
+    /**
+     * Method to check the existance of a template file.
+     *
+     * @param templateID The name of a template.
+     * @return true, of the template exists - otherwise false.
+     */
+    public boolean templateExists(String templateID){
+        Template template = getTemplateByName(templateID);
+        return template != null;
+    }
+
+
+    /**
+     * Method which starts searching for meta information file and mapfile
+     * generation.
+     */
+    protected void generate()
+    throws    FileNotFoundException
+    {
+        File[]        userDirs = getUserDirs();
+        List<LayerInfo> layers = parseLayers(userDirs);
+
+        logger.info("Found " + layers.size() + " layers for user mapfile.");
+
+        writeMapfile(layers);
+    }
+
+
+    /**
+     * Returns the VelocityEngine used for the template mechanism.
+     *
+     * @return the velocity engine.
+     */
+    protected VelocityEngine getVelocityEngine() {
+        if (velocityEngine == null) {
+            velocityEngine = new VelocityEngine();
+            try {
+                setupVelocity(velocityEngine);
+            }
+            catch (Exception e) {
+                logger.error(e, e);
+                return null;
+            }
+        }
+        return velocityEngine;
+    }
+
+
+    /**
+     * Initialize velocity.
+     *
+     * @param engine Velocity engine.
+     * @throws Exception if an error occured while initializing velocity.
+     */
+    protected void setupVelocity(VelocityEngine engine)
+    throws Exception
+    {
+        engine.setProperty(
+            "input.encoding",
+            "UTF-8");
+
+        engine.setProperty(
+            VelocityEngine.RUNTIME_LOG,
+            FLYSUtils.getXPathString(FLYSUtils.XPATH_VELOCITY_LOGFILE));
+
+        engine.setProperty(
+            "resource.loader",
+            "file");
+
+        engine.setProperty(
+            "file.resource.loader.path",
+            FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_TEMPLATE_PATH));
+
+        engine.init();
+    }
+
+
+    /**
+     * Returns a template specified by <i>model</i>.
+     *
+     * @param model The name of the template.
+     * @return a template.
+     */
+    protected Template getTemplateByName(String model) {
+        if (model.indexOf(".vm") < 0) {
+            model = model.concat(".vm");
+        }
+
+        try {
+            VelocityEngine engine = getVelocityEngine();
+            if (engine == null) {
+                logger.error("Error while fetching VelocityEngine.");
+                return null;
+            }
+
+            return engine.getTemplate(model);
+        }
+        catch (Exception e) {
+            logger.warn(e, e);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the mapfile  template.
+     *
+     * @return the mapfile template.
+     * @throws Exception if an error occured while reading the configuration.
+     */
+    protected Template getMapfileTemplate()
+    throws Exception
+    {
+        String mapfileName = FLYSUtils.getXPathString(
+            FLYSUtils.XPATH_MAPFILE_TEMPLATE);
+
+        return getTemplateByName(mapfileName);
+    }
+
+
+    /**
+     * Returns the base directory storing the shapefiles.
+     *
+     * @return the shapefile base directory.
+     *
+     * @throws FileNotFoundException if no shapefile path is found or
+     * configured.
+     */
+    protected File getShapefileBaseDir()
+    throws    FileNotFoundException
+    {
+        if (shapefileDirectory == null) {
+            String path = FLYSUtils.getXPathString(
+                FLYSUtils.XPATH_SHAPEFILE_DIR);
+
+            if (path != null) {
+                shapefileDirectory = new File(path);
+            }
+
+            if (shapefileDirectory == null || !shapefileDirectory.exists()) {
+                throw new FileNotFoundException("No shapefile directory given");
+            }
+        }
+
+        return shapefileDirectory;
+    }
+
+
+    protected File[] getUserDirs()
+    throws    FileNotFoundException
+    {
+        File   baseDir      = getShapefileBaseDir();
+        File[] artifactDirs = baseDir.listFiles();
+
+        // TODO ONLY RETURN DIRECTORIES OF THE SPECIFIED USER
+
+        return artifactDirs;
+    }
+
+
+
+    protected List<LayerInfo> parseLayers(File[] dirs) {
+        List<LayerInfo> layers = new ArrayList<LayerInfo>();
+
+        for (File dir: dirs) {
+            LayerInfo layer = parseUeskLayer(dir);
+
+            if (layer != null && layer.getData() != null) {
+                layers.add(layer);
+            }
+        }
+
+        return layers;
+    }
+
+
+    protected LayerInfo parseUeskLayer(File dir) {
+        File uesk = new File(dir, WSPLGEN_RESULT_SHAPE);
+
+        if (!uesk.exists() || !uesk.isFile()) {
+            return null;
+        }
+
+        return new LayerInfo(uesk, "POLYGON", dir.getName(), dir.getName());
+    }
+
+
+    protected LayerInfo parseBarriersLayers(File dir) {
+        // TODO IMPLEMENT ME
+
+        return null;
+    }
+
+
+    /**
+     * Creates a mapfile with the layer information stored in <i>layers</i>.
+     *
+     * @param layers Layer information.
+     */
+    protected void writeMapfile(List<LayerInfo> layers) {
+        String tmpMapName = "mapfile" + new Date().getTime();
+
+        File mapfile = new File(
+            FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPFILE_PATH));
+
+        File   tmp     = null;
+        Writer writer  = null;
+
+        try {
+            tmp = new File(mapfile.getParent(), tmpMapName);
+            tmp.createNewFile();
+
+            writer = new FileWriter(tmp);
+
+            VelocityContext context = new VelocityContext();
+            context.put("MAPSERVERURL",
+                FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_URL));
+            context.put("SHAPEFILEPATH",
+                FLYSUtils.getXPathString(FLYSUtils.XPATH_SHAPEFILE_DIR));
+            context.put("LAYERS", fillLayerTemplates(layers));
+
+            Template mapTemplate = getMapfileTemplate();
+            if (mapTemplate == null) {
+                logger.warn("No mapfile template found.");
+                return;
+            }
+
+            mapTemplate.merge(context, writer);
+
+            // we need to create a temporary mapfile first und rename it into
+            // real mapfile because we don't run into race conditions on this
+            // way. (iw)
+            tmp.renameTo(mapfile);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+
+                if (tmp.exists()) {
+                    tmp.delete();
+                }
+            }
+            catch (IOException ioe) {
+                logger.debug(ioe, ioe);
+            }
+        }
+    }
+
+
+    protected List<String> fillLayerTemplates(List<LayerInfo> layers) {
+        List<String> evaluated = new ArrayList<String>(layers.size());
+
+        for (LayerInfo layer: layers) {
+            StringWriter writer = new StringWriter();
+
+            VelocityContext context = new VelocityContext();
+            context.put("LAYER", layer);
+
+            Template t = getTemplateByName("layer.vm");
+            if (t == null) {
+                logger.warn("No 'layer.vm' template found.");
+                return evaluated;
+            }
+
+            t.merge(context, writer);
+
+            evaluated.add(writer.toString());
+        }
+
+        return evaluated;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org