# HG changeset patch # User Ingo Weinzierl # Date 1315231443 0 # Node ID da3c3e286c881348abeb36f34ea1c31c2afd2aa7 # Parent 727c53fd0dc7e6ff4c899e588b46bedaaf6c429d Introduced a MapfileGenerator that creates Mapserver specific mapfiles based on WSPLGEN results read from filesystem. flys-artifacts/trunk@2644 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/ChangeLog --- 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 + + * 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 * src/main/java/de/intevation/flys/artifacts/model/WSPLGENJob.java: diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/doc/conf/conf.xml --- 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.--> + + + + + + + + + + diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/doc/conf/mapserver/layer.vm --- /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 diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/doc/conf/mapserver/mapfile.vm --- /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 diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/pom.xml --- 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 @@ gt-geojson 2.7.2 + + org.apache.velocity + velocity + 1.7 + diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/LayerInfo.java --- /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 : diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/FloodMapState.java --- 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()); diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/src/main/java/de/intevation/flys/utils/FLYSUtils.java --- 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"); diff -r 727c53fd0dc7 -r da3c3e286c88 flys-artifacts/src/main/java/de/intevation/flys/utils/MapfileGenerator.java --- /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 MapServer. + * + * @author Ingo Weinzierl + */ +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.
+ * NOTE: 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 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 model. + * + * @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 parseLayers(File[] dirs) { + List layers = new ArrayList(); + + 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 layers. + * + * @param layers Layer information. + */ + protected void writeMapfile(List 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 fillLayerTemplates(List layers) { + List evaluated = new ArrayList(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 :