view flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java @ 4573:b87073a05f9d

flys-client: Patch to render combobox options as clickable links. The way of passing data arguments to the links and further to the Artifact feeding service is somewhat hacked and should be refactored (later...).
author Christian Lins <christian.lins@intevation.de>
date Tue, 27 Nov 2012 12:50:10 +0100
parents 3dc26ec2558d
children fc52ee878412
line wrap: on
line source
package de.intevation.flys.utils;

import de.intevation.artifacts.CallContext;
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.map.WMSDBLayerFacet;
import de.intevation.flys.artifacts.model.map.WMSLayerFacet;
import de.intevation.flys.artifacts.model.map.WSPLGENLayerFacet;
import de.intevation.flys.artifacts.resources.Resources;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
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 org.geotools.data.shapefile.ShpFiles;
import org.geotools.data.shapefile.shp.ShapefileHeader;
import org.geotools.data.shapefile.shp.ShapefileReader;

/**
 * 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 WSPLGEN_USER_SHAPE     = "user-rgd.shp";

    public static final String WSPLGEN_LAYER_TEMPLATE = "wsplgen_layer.vm";
    public static final String SHP_LAYER_TEMPLATE = "shapefile_layer.vm";
    public static final String DB_LAYER_TEMPLATE  = "db_layer.vm";

    public static final String MS_WSPLGEN_PREFIX   = "wsplgen-";
    public static final String MS_BARRIERS_PREFIX  = "barriers-";
    public static final String MS_LINE_PREFIX      = "lines-";
    public static final String MS_POLYGONS_PREFIX  = "polygons-";
    public static final String MS_LAYER_PREFIX     = "ms_layer-";
    public static final String MS_USERSHAPE_PREFIX = "user-";

    protected static final long SLEEPTIME = 10 * 1000L; // 10 seconds

    private static Logger logger = Logger.getLogger(MapfileGenerator.class);

    private static MapfileGenerator instance;

    private File shapefileDirectory;

    private VelocityEngine velocityEngine;
    private final 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<String> 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();
    }


    protected VelocityContext getVelocityContext() {
        VelocityContext context = new VelocityContext();

        try {
            context.put("MAPSERVERURL",
                FLYSUtils.getXPathString(FLYSUtils.XPATH_MAPSERVER_URL));
            context.put("SHAPEFILEPATH",
                getShapefileBaseDir().getCanonicalPath());
            context.put("CONFIGDIR",
                Config.getConfigDirectory().getCanonicalPath());
        }
        catch (FileNotFoundException fnfe) {
            // this is bad
        }
        catch (IOException ioe) {
            // this is also bad
        }

        return context;
    }


    /**
     * 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.
     */
    public 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.mkdirs();
            }
        }

        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<String> parseLayers(File[] dirs) {
        List<String> layers = new ArrayList<String>();

        for (File dir: dirs) {
            File[] layerFiles = dir.listFiles(new FilenameFilter() {
                @Override
                public boolean accept(File directory, String name) {
                    return name.startsWith(MS_LAYER_PREFIX);
                }
            });

            for (File layer: layerFiles) {
                try {
                    layers.add(layer.getCanonicalPath());
                }
                catch (IOException ioe) {
                    logger.warn(ioe, ioe);
                }
            }
        }

        return layers;
    }


    /**
     * 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,
        WSPLGENLayerFacet wms,
        String        style,
        CallContext context
    ) throws FileNotFoundException, IOException
    {
        logger.debug("createUeskLayer");

        LayerInfo layerinfo = new LayerInfo();
        layerinfo.setName(MS_WSPLGEN_PREFIX + flys.identifier());
        layerinfo.setType("POLYGON");
        layerinfo.setDirectory(flys.identifier());
        layerinfo.setData(WSPLGEN_RESULT_SHAPE);
        layerinfo.setTitle(Resources.getMsg(Resources.getLocale(context.getMeta()),
                                            "floodmap.uesk",
                                            "Floodmap"));
        layerinfo.setStyle(style);
        layerinfo.setSrid(wms.getSrid());

        String name = MS_LAYER_PREFIX + wms.getName();

        Template template = getTemplateByName(WSPLGEN_LAYER_TEMPLATE);
        if (template == null) {
            logger.warn("Template '" + WSPLGEN_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);

        createBarriersLineLayer(flys, wms);
        createBarriersPolygonLayer(flys, wms);
    }


    protected void createBarriersLineLayer(
        FLYSArtifact  flys,
        WMSLayerFacet wms
    )
    throws FileNotFoundException, IOException
    {
        String uuid       = flys.identifier();
        String group      = MS_BARRIERS_PREFIX + uuid;
        String groupTitle = "I18N_BARRIERS_TITLE";

        File dir  = new File(getShapefileBaseDir(), uuid);
        File test = new File(dir, WSPLGEN_LINES_SHAPE);

        if (!test.exists() || !test.canRead()) {
            logger.debug("No barrier line layer existing.");
            return;
        }

        LayerInfo lineInfo = new LayerInfo();
        lineInfo.setName(MS_LINE_PREFIX + uuid);
        lineInfo.setType("LINE");
        lineInfo.setDirectory(uuid);
        lineInfo.setData(WSPLGEN_LINES_SHAPE);
        lineInfo.setTitle("I18N_LINE_SHAPE");
        lineInfo.setGroup(group);
        lineInfo.setGroupTitle(groupTitle);
        lineInfo.setSrid(wms.getSrid());

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

        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);
        }
    }


    protected void createBarriersPolygonLayer(
        FLYSArtifact  flys,
        WMSLayerFacet wms
    )
    throws FileNotFoundException, IOException
    {
        String uuid       = flys.identifier();
        String group      = uuid + MS_BARRIERS_PREFIX;
        String groupTitle = "I18N_BARRIERS_TITLE";

        File dir  = new File(getShapefileBaseDir(), uuid);
        File test = new File(dir, WSPLGEN_POLYGONS_SHAPE);

        if (!test.exists() || !test.canRead()) {
            logger.debug("No barrier line layer existing.");
            return;
        }

        LayerInfo polygonInfo = new LayerInfo();
        polygonInfo.setName(MS_POLYGONS_PREFIX + uuid);
        polygonInfo.setType("POLYGON");
        polygonInfo.setDirectory(uuid);
        polygonInfo.setData(WSPLGEN_POLYGONS_SHAPE);
        polygonInfo.setTitle("I18N_POLYGON_SHAPE");
        polygonInfo.setGroup(group);
        polygonInfo.setGroupTitle(groupTitle);
        polygonInfo.setSrid(wms.getSrid());

        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(polygonInfo, dir, namePolygons, tpl);
        }
        catch (FileNotFoundException fnfe) {
            logger.error(fnfe, fnfe);
            logger.warn("Unable to write layer: " + namePolygons);
        }
    }


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

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

        if (!test.exists() || !test.canRead()) {
            logger.debug("No user layer existing.");
            return;
        }

        File userShape = new File(dir, WSPLGEN_USER_SHAPE);
        ShpFiles sf = new ShpFiles(userShape);
        ShapefileReader sfr = new ShapefileReader(sf, true, false, null);
        ShapefileHeader sfh = sfr.getHeader();

        String group      = uuid + MS_USERSHAPE_PREFIX;
        String groupTitle = "I18N_USER_SHAPE_TITLE";

        LayerInfo info = new LayerInfo();
        info.setName(MS_USERSHAPE_PREFIX + uuid);
        if (sfh.getShapeType().isLineType()) {
            info.setType("LINE");
        }
        else if (sfh.getShapeType().isPolygonType()) {
            info.setType("POLYGON");
        }
        else {
            return;
        }
        info.setDirectory(uuid);
        info.setData(WSPLGEN_USER_SHAPE);
        info.setTitle("I18N_USER_SHAPE");
        info.setGroup(group);
        info.setGroupTitle(groupTitle);
        info.setSrid(wms.getSrid());

        String nameUser = MS_LAYER_PREFIX + wms.getName();

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

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

    }


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

        LayerInfo layerinfo = new LayerInfo();
        layerinfo.setName(wms.getName() + "-" + flys.identifier());
        layerinfo.setType(wms.getGeometryType());
        layerinfo.setFilter(wms.getFilter());
        layerinfo.setData(wms.getData());
        layerinfo.setTitle(wms.getDescription());
        layerinfo.setStyle(style);
        if(wms.getExtent() != null) {
            layerinfo.setExtent(GeometryUtils.jtsBoundsToOLBounds(wms.getExtent()));
        }
        layerinfo.setConnection(wms.getConnection());
        layerinfo.setConnectionType(wms.getConnectionType());
        layerinfo.setLabelItem(wms.getLabelItem());
        layerinfo.setSrid(wms.getSrid());

        String name = MS_LAYER_PREFIX + wms.getName();

        Template template = getTemplateByName(DB_LAYER_TEMPLATE);
        if (template == null) {
            logger.warn("Template '" + DB_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 snippet which might be included in the mapfile.
     *
     * @param layerinfo A LayerInfo object that contains all necessary
     * information to build a Mapserver LAYER section.
     * @param dir The base dir for the LAYER snippet.
     * @param filename The name of the file that is written.
     * @param tpl The Velocity template which is used to create the LAYER
     * section.
     */
    protected void writeLayer(
        LayerInfo layerinfo,
        File      dir,
        String    filename,
        Template  tpl
    )
    throws    FileNotFoundException
    {
        if (logger.isDebugEnabled()) {
            logger.debug("Write layer for:");
            logger.debug("   directory: " + dir.getName());
            logger.debug("   name:      " + filename);
        }

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

        try {
            writer = new FileWriter(layer);

            VelocityContext context = getVelocityContext();
            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<String> 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 = getVelocityContext();
            context.put("LAYERS", 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);
            }
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org