view flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java @ 1775:0156105222c9

Improved the MapfileGenerator. It offers methods to create barrier and wsplgen layer files for mapserver. flys-artifacts/trunk@3097 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Fri, 28 Oct 2011 08:36:32 +0000
parents 055f32a5388a
children ef2300b450bf
line wrap: on
line source
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.artifacts.common.utils.Config;

import de.intevation.flys.artifacts.FLYSArtifact;
import de.intevation.flys.artifacts.model.LayerInfo;
import de.intevation.flys.artifacts.model.WMSLayerFacet;

/**
 * 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";
    public static final String WSPLGEN_LINES_SHAPE    = "barrier_lines.shp";
    public static final String WSPLGEN_POLYGONS_SHAPE = "barrier_polygons.shp";

    public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm";

    public static final String MS_WSPLGEN_POSTFIX  = "-wsplgen";
    public static final String MS_BARRIERS_POSTFIX = "-barriers";
    public static final String MS_LINE_POSTFIX     = "-lines";
    public static final String MS_POLYGONS_POSTFIX = "-polygons";
    public static final String MS_LAYER_PREFIX     = "ms_layer-";

    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());
        }
        catch (IOException ioe) {
            logger.error(ioe, ioe);
        }
        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, IOException
    {
        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, IOException
    {
        if (shapefileDirectory == null) {
            String path = FLYSUtils.getXPathString(
                FLYSUtils.XPATH_SHAPEFILE_DIR);

            if (path != null) {
                shapefileDirectory = new File(path);
            }

            if (shapefileDirectory == null) {
                throw new FileNotFoundException("No shapefile directory given");
            }

            if (!shapefileDirectory.exists()) {
                shapefileDirectory.createNewFile();
            }
        }

        return shapefileDirectory;
    }


    protected File[] getUserDirs()
    throws    FileNotFoundException, IOException
    {
        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) {
                logger.debug("   Add WSPLGEN layer.");
                layers.add(layer);
            }

            List<LayerInfo> barriers = parseBarriersLayers(dir);
            int num = barriers != null ? barriers.size() : 0;
            if (num > 0) {
                if (barriers.get(0).getData() != null) {
                    logger.debug("   Add " + num + " BARRIERS layers.");
                    layers.addAll(barriers);
                }
            }
        }

        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(
            dir.getName() + MS_WSPLGEN_POSTFIX,
            "POLYGON",
            dir.getName(),
            WSPLGEN_RESULT_SHAPE,
            "I18N_WSPLGEN_RESULT");
    }


    /**
     * Creates a layer file used for Mapserver's mapfile which represents the
     * floodmap.
     *
     * @param flys The FLYSArtifact that owns <i>wms</i>.
     * @param wms The WMSLayerFacet that contains information for the layer.
     */
    public void createUeskLayer(FLYSArtifact flys, WMSLayerFacet wms)
    throws FileNotFoundException, IOException
    {
        logger.debug("createUeskLayer");

        LayerInfo layerinfo = new LayerInfo(
            flys.identifier() + MS_WSPLGEN_POSTFIX,
            "POLYGON",
            flys.identifier(),
            WSPLGEN_RESULT_SHAPE,
            "I18N_WSPLGEN_RESULT");

        String name = MS_LAYER_PREFIX + wms.getName();

        Template template = getTemplateByName(SHP_LAYER_TEMPLATE);
        if (template == null) {
            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
            return;
        }

        try {
            File dir = new File(getShapefileBaseDir(), flys.identifier());
            writeLayer(layerinfo, dir, name, template);
        }
        catch (FileNotFoundException fnfe) {
            logger.error(fnfe, fnfe);
            logger.warn("Unable to write layer: " + name);
        }
    }


    /**
     * Creates a layer file used for Mapserver's mapfile which represents the
     * user defined barriers.
     *
     * @param flys The FLYSArtifact that owns <i>wms</i>.
     * @param wms The WMSLayerFacet that contains information for the layer.
     */
    public void createBarriersLayer(FLYSArtifact flys, WMSLayerFacet wms)
    throws FileNotFoundException, IOException
    {
        logger.debug("createBarriersLayer");

        String uuid = flys.identifier();
        File   dir  = new File(getShapefileBaseDir(), uuid);

        String group      = uuid + MS_BARRIERS_POSTFIX;
        String groupTitle = "I18N_BARRIERS_TITLE";

        LayerInfo lineInfo = new LayerInfo(
            uuid + MS_LINE_POSTFIX,
            "LINE",
            uuid,
            WSPLGEN_LINES_SHAPE,
            "I18N_LINE_SHAPE",
            group,
            groupTitle);

        LayerInfo polygonInfo = new LayerInfo(
            uuid + MS_POLYGONS_POSTFIX,
            "POLYGON",
            uuid,
            WSPLGEN_POLYGONS_SHAPE,
            "I18N_POLYGON_SHAPE",
            group,
            groupTitle);

        String nameLines    = MS_LAYER_PREFIX + wms.getName() + "-lines";
        String namePolygons = MS_LAYER_PREFIX + wms.getName() + "-polygons";

        Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
        if (tpl == null) {
            logger.warn("Template '" + SHP_LAYER_TEMPLATE + "' found.");
            return;
        }

        try {
            writeLayer(lineInfo, dir, nameLines, tpl);
        }
        catch (FileNotFoundException fnfe) {
            logger.error(fnfe, fnfe);
            logger.warn("Unable to write layer: " + nameLines);
        }

        try {
            writeLayer(polygonInfo, dir, namePolygons, tpl);
        }
        catch (FileNotFoundException fnfe) {
            logger.error(fnfe, fnfe);
            logger.warn("Unable to write layer: " + nameLines);
        }
    }


    protected List<LayerInfo> parseBarriersLayers(File dir) {
        List<LayerInfo> barriers = new ArrayList<LayerInfo>(2);

        String group      = dir.getName() + MS_BARRIERS_POSTFIX;
        String groupTitle = "I18N_BARRIERS_TITLE";

        File lines    = new File(dir, WSPLGEN_LINES_SHAPE);
        File polygons = new File(dir, WSPLGEN_POLYGONS_SHAPE);

        if (lines.exists() || lines.isFile()) {
            barriers.add(
                new LayerInfo(
                    dir.getName() + MS_LINE_POSTFIX,
                    "LINE",
                    dir.getName(),
                    WSPLGEN_LINES_SHAPE,
                    "I18N_LINE_SHAPE",
                    group,
                    groupTitle));
        }

        if (polygons.exists() || polygons.isFile()) {
            barriers.add(
                new LayerInfo(
                    dir.getName() + MS_POLYGONS_POSTFIX,
                    "POLYGON",
                    dir.getName(),
                    WSPLGEN_POLYGONS_SHAPE,
                    "I18N_POLYGON_SHAPE",
                    group,
                    groupTitle));
        }

        return barriers;
    }


    protected void writeLayer(
        LayerInfo layerinfo,
        File      dir,
        String    name,
        Template  tpl
    )
    throws    FileNotFoundException
    {
        if (logger.isDebugEnabled()) {
            logger.debug("Write layer for:");
            logger.debug("   directory: " + dir.getName());
            logger.debug("   name:      " + name);
        }

        File   layer  = new File(dir, name);
        Writer writer = null;

        try {
            writer = new FileWriter(layer);

            VelocityContext context = new VelocityContext();
            context.put("LAYER", layerinfo);

            tpl.merge(context, writer);
        }
        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();
                }
            }
            catch (IOException ioe) {
                logger.debug(ioe, ioe);
            }
        }
    }


    /**
     * 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",
                getShapefileBaseDir().getCanonicalPath());
            context.put("CONFIGDIR",
                Config.getConfigDirectory().getCanonicalPath());
            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