diff gnv-artifacts/src/main/java/de/intevation/gnv/utils/MapfileGenerator.java @ 875:5e9efdda6894

merged gnv-artifacts/1.0
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:13:56 +0200
parents 47578d91c4f0
children f953c9a559d8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/MapfileGenerator.java	Fri Sep 28 12:13:56 2012 +0200
@@ -0,0 +1,588 @@
+package de.intevation.gnv.utils;
+
+import de.intevation.artifactdatabase.Config;
+import de.intevation.artifactdatabase.XMLUtils;
+
+import de.intevation.artifacts.ArtifactNamespaceContext;
+
+import de.intevation.gnv.wms.LayerInfo;
+
+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 javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+
+import org.apache.velocity.app.VelocityEngine;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * 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
+{
+    /**
+     * The configured template path.
+     */
+    public static final String TEMPLATE_PATH =
+        "/artifact-database/gnv/map-generator/templates/path/text()";
+
+    /**
+     * The configured template mapfile.
+     */
+    public static final String TEMPLATE_MAPFILE =
+        "/artifact-database/gnv/map-generator/templates/maptemplate/text()";
+
+    /**
+     * The configured mapfile path.
+     */
+    public static final String MAPFILE_PATH =
+        "/artifact-database/gnv/map-generator/mapfile/@path";
+
+    /**
+     * The configured shapefile base directory where the subdirectories for each
+     * artifact are stored.
+     */
+    public static final String SHAPEFILE_BASE_DIR =
+        "/artifact-database/gnv/shapefile-directory/@path";
+
+    /**
+     * The configured velocity logfile.
+     */
+    public static final String VELOCITY_LOGFILE =
+        "/artifact-database/velocity/logfile/@path";
+
+    /**
+     * The URL for calling the mapserver
+     */
+    public static final String MAPSERVER_URL =
+        "/artifact-database/mapserver/server/@path";
+
+    /**
+     * The name of the file storing meta information coresponding to shapefiles.
+     */
+    public static final String META_FILE_NAME = "meta.xml";
+
+    /**
+     * The XPath to layer nodes in the meta information xml file.
+     */
+    public static final String XPATH_LAYER        = "/art:meta/art:layer";
+
+    public static final String XPATH_LAYER_NAME   = "art:name";
+    public static final String XPATH_LAYER_TITLE  = "art:title";
+    public static final String XPATH_LAYER_TYPE   = "art:type";
+    public static final String XPATH_LAYER_DATA   = "art:data";
+    public static final String XPATH_LAYER_STATUS = "art:status";
+    public static final String XPATH_LAYER_MODEL  = "art:model";
+
+    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 String mapServerUrl;
+    private String shapefileDirectory;
+    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) {
+        // TODO IMPLEMENT ME
+    }
+
+
+    /**
+     * 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.");
+        }
+        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() {
+        File basedir       = new File(getShapefileBaseDir());
+        List layers        = new ArrayList();
+        searchMetaInformation(basedir, layers);
+        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,
+            getVelocityLogfile());
+
+        engine.setProperty(
+            "resource.loader",
+            "file");
+
+        engine.setProperty(
+            "file.resource.loader.path",
+            getTemplateBaseDir());
+
+        engine.init();
+    }
+
+
+    /**
+     * Returns the path specifying the logfile for velocity.
+     *
+     * @return Velocity logfile path.
+     */
+    protected String getVelocityLogfile() {
+        if (velocityLogfile == null)
+            velocityLogfile = Config.getStringXPath(VELOCITY_LOGFILE);
+
+        return velocityLogfile;
+    }
+
+
+    /**
+     * Returns the base directory storing the templates.
+     *
+     * @return the template base directory.
+     */
+    protected String getTemplateBaseDir() {
+        if (templatePath == null) {
+            templatePath = Config.getStringXPath(TEMPLATE_PATH);
+            templatePath = Config.replaceConfigDir(templatePath);
+        }
+
+        return templatePath;
+    }
+
+
+    /**
+     * Returns the URL for calling the MapServer.
+     *
+     * @return the url for calling the MapServer.
+     */
+    protected String getMapServerUrl() {
+        if (mapServerUrl == null) {
+            mapServerUrl = Config.getStringXPath(MAPSERVER_URL);
+            mapServerUrl = Config.replaceConfigDir(mapServerUrl);
+        }
+
+        return mapServerUrl;
+    }
+
+
+    /**
+     * 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 = Config.getStringXPath(TEMPLATE_MAPFILE);
+        return getTemplateByName(mapfileName);
+    }
+
+
+    /**
+     * Returns the base directory storing the shapefiles.
+     *
+     * @return the shapefile base directory.
+     */
+    protected String getShapefileBaseDir() {
+        if (shapefileDirectory == null) {
+            shapefileDirectory = Config.getStringXPath(SHAPEFILE_BASE_DIR);
+            shapefileDirectory = Config.replaceConfigDir(shapefileDirectory);
+        }
+
+        return shapefileDirectory;
+    }
+
+
+    /**
+     * Returns the mapfile.
+     *
+     * @return the mapfile.
+     */
+    protected File getMapfile() {
+        if (mapfile == null) {
+            String tmp = Config.getStringXPath(MAPFILE_PATH);
+            tmp        = Config.replaceConfigDir(tmp);
+            mapfile    = new File(tmp);
+        }
+
+        return mapfile;
+    }
+
+
+    /**
+     * Search for meta information file in <i>file</i> and store the layer
+     * information in <i>store</i> if a meta file was found.
+     *
+     * @param file The root directory to be searched for meta files.
+     * @param store A list storing <code>LayerInfo</code> objects.
+     */
+    protected void searchMetaInformation(File file, List store) {
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+
+            if (files != null && files.length != 0) {
+                int size = files.length;
+                for (File tmp: files) {
+                    searchMetaInformation(tmp, store);
+                }
+            }
+        }
+        else if (file.getName().equals(META_FILE_NAME)) {
+            LayerInfo[] info = parseMeta(file);
+
+            int infoSize = info.length;
+            for (int j = 0; j < infoSize; j++) {
+                if (info[j] != null) {
+                    store.add(info[j]);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Parses a meta information file and returns necessary information as
+     * <code>LayerInfo</code> array.
+     *
+     * @param file Meta information file.
+     * @return Array of LayerInfo objects.
+     */
+    protected LayerInfo[] parseMeta(File file) {
+        Document meta = XMLUtils.parseDocument(file);
+        List layers   = new ArrayList();
+
+        NodeList layerset = (NodeList) XMLUtils.xpath(
+            meta,
+            XPATH_LAYER,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int size = layerset.getLength();
+        for (int i = 0; i < size; i++) {
+            LayerInfo info = parseLayer(layerset.item(i));
+
+            if (info != null && !info.isEmpty() && !info.isBroken()) {
+                layers.add(info);
+            }
+            else {
+                logger.warn("Found broken LayerInfo object.");
+            }
+        }
+
+        return (LayerInfo[]) layers.toArray(new LayerInfo[layers.size()]);
+    }
+
+
+    /**
+     * Parses a node storing layer information and returns them.
+     *
+     * @param layer Node storing information about a layer.
+     * @return a LayerInfo object.
+     */
+    protected LayerInfo parseLayer(Node layer) {
+        LayerInfo info  = new LayerInfo();
+
+        String name = parseLayerAttr(layer, XPATH_LAYER_NAME);
+        if (name != null && !name.equals("")) {
+            info.setName(name);
+        }
+
+        String title = parseLayerAttr(layer, XPATH_LAYER_TITLE);
+        if (title != null && !title.equals("")) {
+            info.setTitle(title);
+        }
+        else {
+            info.setTitle(name);
+        }
+
+        String model = parseLayerAttr(layer, XPATH_LAYER_MODEL);
+        if (model != null && !model.equals("")) {
+            info.setModel(model);
+        }
+
+        String type = parseLayerAttr(layer, XPATH_LAYER_TYPE);
+        if (type != null && !type.equals("")) {
+            info.setType(type);
+        }
+
+        String data = parseLayerAttr(layer, XPATH_LAYER_DATA);
+        if (data != null && !data.equals("")) {
+            info.setData(data);
+        }
+
+        String status = parseLayerAttr(layer, XPATH_LAYER_STATUS);
+        if (status != null && !status.equals("")) {
+            info.setStatus(status);
+        }
+
+        return info;
+    }
+
+
+    /**
+     * Parses attributes in layer nodes.
+     *
+     * @param node A node containing layer information.
+     * @param xpath XPath specifying an attribute.
+     * @return Attribute as string.
+     */
+    protected String parseLayerAttr(Node node, String xpath) {
+        return (String) XMLUtils.xpath(
+            node,
+            xpath,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    /**
+     * Creates a mapfile with the layer information stored in <i>layers</i>.
+     *
+     * @param layers Layer information.
+     */
+    protected void writeMapfile(List layers) {
+        String tmpMapName = "mapfile" + new Date().getTime();
+
+        int layersize         = layers.size();
+        StringBuilder sb      = new StringBuilder();
+        StringWriter sw       = null;
+        LayerInfo info        = null;
+
+        for (int i = 0; i < layersize; i++) {
+            sw   = new StringWriter();
+            info = (LayerInfo) layers.get(i);
+
+            Template layerTemplate  = getTemplateByName(info.getModel());
+            VelocityContext context = new VelocityContext();
+            context.put("info", info);
+
+            try {
+                layerTemplate.merge(context, sw);
+                sb.append(sw.toString());
+            }
+            catch (IOException ioe) {
+                logger.warn("Error while filling layer template.");
+                logger.warn(ioe, ioe);
+            }
+        }
+
+        File   map    = getMapfile();
+        Writer writer = null;
+        File   tmp    = null;
+
+        try {
+            tmp = new File(map.getParent(), tmpMapName);
+
+            tmp.createNewFile();
+            writer   = new FileWriter(tmp);
+
+            VelocityContext context = new VelocityContext();
+            context.put("MAPSERVERURL", getMapServerUrl());
+            context.put("LAYERS", sb.toString());
+
+            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(map);
+        }
+        catch (FileNotFoundException fnfe) {
+            logger.error(fnfe, fnfe);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe, ioe);
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+        finally {
+            try {
+                // close file writer
+                if (writer != null) {
+                    writer.close();
+                }
+
+                // remove temporary mapfile if an error occured and it still
+                // exists
+                if (tmp.exists()) {
+                    tmp.delete();
+                }
+            }
+            catch (IOException ioe) {
+                logger.debug(ioe, ioe);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org